def generateElements(cls, fieldmodule, coordinates, nodeId, startElementIdentifier=1): """ Create valve elements from nodes. :param fieldmodule: Zinc fieldmodule to create elements in. :param coordinates: Coordinate field to define. :param nodeId: Node identifiers returned by generateNodes(). Indexed by [n3=wall][n2=inlet->outlet][n1=around]. :param startElementIdentifier: First element identifier to use. :return: next elementIdentifier, elementId[e1]. """ elementIdentifier = startElementIdentifier elementId = [] elementsCountAround = 6 # fixed useCrossDerivatives = False mesh = fieldmodule.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft, [1], []) scalefactors = [-1.0] tricubichermite.setEftLinearDerivative(eft, [3, 7], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft, [4, 8], Node.VALUE_LABEL_D_DS3, 4, 8, 1) elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate.defineField(coordinates, -1, eft) for ea in range(elementsCountAround): eb = (ea + 1) % elementsCountAround nids = [ nodeId[0][0][ea], nodeId[0][0][eb], nodeId[0][1][ea], nodeId[0][1][eb], nodeId[1][0][ea], nodeId[1][0][eb], nodeId[1][1][ea], nodeId[1][1][eb] ] element = mesh.createElement(elementIdentifier, elementtemplate) element.setNodesByIdentifier(eft, nids) element.setScaleFactors(eft, scalefactors) elementId.append(elementIdentifier) elementIdentifier += 1 return elementIdentifier, elementId
def generateBaseMesh(cls, region, options): """ Generate the base tricubic Hermite or bicubic Hermite linear mesh. :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: [] empty list of AnnotationGroup """ elementsCountAround = options['Number of elements around'] elementsCountUp = options['Number of elements up'] elementsCountThroughWall = options['Number of elements through wall'] useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not (options['Use linear through wall']) excludeBottomRows = options['Exclude bottom rows'] excludeTopRows = options['Exclude top rows'] wallThickness = options['Wall thickness'] wallThicknessRatioApex = options['Wall thickness ratio apex'] lengthRatio = options['Length ratio'] elementLengthRatioEquatorApex = options[ 'Element length ratio equator/apex'] fm = region.getFieldmodule() fm.beginChange() coordinates = findOrCreateFieldCoordinates(fm) nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) nodetemplateApex = nodes.createNodetemplate() nodetemplateApex.defineField(coordinates) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCubicHermiteThroughWall: nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) if useCrossDerivatives: nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) if useCubicHermiteThroughWall: nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) 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) else: nodetemplate = nodetemplateApex mesh = fm.findMeshByDimension(3) if useCubicHermiteThroughWall: eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives) else: eftfactory = eftfactory_bicubichermitelinear( mesh, useCrossDerivatives) eft = eftfactory.createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate.defineField(coordinates, -1, eft) cache = fm.createFieldcache() # create nodes nodeIdentifier = 1 radiansPerElementAround = 2.0 * math.pi / elementsCountAround x = [0.0, 0.0, 0.0] dx_ds1 = [0.0, 0.0, 0.0] dx_ds2 = [0.0, 0.0, 0.0] dx_ds3 = [0.0, 0.0, 0.0] zero = [0.0, 0.0, 0.0] # pre-calculate positions and tangent/normal vectors up (elementsCountUp + 1) node layers outerWidth = 0.5 outerLength = outerWidth * lengthRatio bOuter = 2.0 / (1.0 + elementLengthRatioEquatorApex / lengthRatio) aOuter = 1.0 - bOuter innerWidth = outerWidth - wallThickness innerLength = outerLength - wallThickness * wallThicknessRatioApex lengthRatioInner = innerLength / innerWidth if ( innerWidth > 0.0) else lengthRatio bInner = 2.0 / (1.0 + elementLengthRatioEquatorApex / lengthRatio) aInner = 1.0 - bInner positionOuterArray = [(0, 0)] * (elementsCountUp + 1) positionInnerArray = [(0, 0)] * (elementsCountUp + 1) radiansUpOuterArray = [0] * (elementsCountUp + 1) radiansUpInnerArray = [0] * (elementsCountUp + 1) vector2OuterArray = [(0, 0)] * (elementsCountUp + 1) vector2InnerArray = [(0, 0)] * (elementsCountUp + 1) for n2 in range(elementsCountUp + 1): if n2 * 2 <= elementsCountUp: xi = n2 * 2 / elementsCountUp else: xi = 2.0 - (n2 * 2 / elementsCountUp) nxiOuter = aOuter * xi * xi + bOuter * xi dnxiOuter = 2.0 * aOuter * xi + bOuter radiansUpOuterArray[ n2] = radiansUpOuter = nxiOuter * math.pi * 0.5 if ( n2 * 2 <= elementsCountUp) else (math.pi - nxiOuter * math.pi * 0.5) dRadiansUpOuter = dnxiOuter * math.pi / elementsCountUp cosRadiansUpOuter = math.cos(radiansUpOuter) sinRadiansUpOuter = math.sin(radiansUpOuter) positionOuterArray[n2] = positionOuter = (outerWidth * sinRadiansUpOuter, -outerLength * cosRadiansUpOuter) vector2OuterArray[n2] = (outerWidth * cosRadiansUpOuter * dRadiansUpOuter, outerLength * sinRadiansUpOuter * dRadiansUpOuter) nxiInner = aInner * xi * xi + bInner * xi dnxiInner = 2.0 * aInner * xi + bInner radiansUpInnerArray[ n2] = radiansUpInner = nxiInner * math.pi * 0.5 if ( n2 * 2 <= elementsCountUp) else (math.pi - nxiInner * math.pi * 0.5) dRadiansUpInner = dnxiInner * math.pi / elementsCountUp cosRadiansUpInner = math.cos(radiansUpInner) sinRadiansUpInner = math.sin(radiansUpInner) positionInnerArray[n2] = positionInner = (innerWidth * sinRadiansUpInner, -innerLength * cosRadiansUpInner) vector2InnerArray[n2] = (innerWidth * cosRadiansUpInner * dRadiansUpInner, innerLength * sinRadiansUpInner * dRadiansUpInner) # now create the nodes for n3 in range(elementsCountThroughWall + 1): n3_fraction = n3 / elementsCountThroughWall for n2 in range(excludeBottomRows, elementsCountUp + 1 - excludeTopRows): positionOuter = positionOuterArray[n2] positionInner = positionInnerArray[n2] position = (positionOuter[0] * n3_fraction + positionInner[0] * (1.0 - n3_fraction), positionOuter[1] * n3_fraction + positionInner[1] * (1.0 - n3_fraction)) radiansUpOuter = radiansUpOuterArray[n2] sinRadiansUpOuter = math.sin(radiansUpOuter) radiansUpInner = radiansUpInnerArray[n2] sinRadiansUpInner = math.sin(radiansUpInner) vector2Outer = vector2OuterArray[n2] vector2Inner = vector2InnerArray[n2] vector2 = (vector2Outer[0] * n3_fraction + vector2Inner[0] * (1.0 - n3_fraction), vector2Outer[1] * n3_fraction + vector2Inner[1] * (1.0 - n3_fraction)) vector3 = ((positionOuter[0] - positionInner[0]) / elementsCountThroughWall, (positionOuter[1] - positionInner[1]) / elementsCountThroughWall) if n2 == 0: # create bottom apex node node = nodes.createNode(nodeIdentifier, nodetemplateApex) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, [0.0, 0.0, position[1]]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, [0.0, vector2[0], 0.0]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, [vector2[0], 0.0, 0.0]) if useCubicHermiteThroughWall: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, [0.0, 0.0, vector3[1]]) nodeIdentifier = nodeIdentifier + 1 elif n2 < elementsCountUp: # create regular rows between apexes for n1 in range(elementsCountAround): radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x = [ position[0] * cosRadiansAround, position[0] * sinRadiansAround, position[1] ] dx_ds1 = [ position[0] * -sinRadiansAround * radiansPerElementAround, position[0] * cosRadiansAround * radiansPerElementAround, 0.0 ] dx_ds2 = [ vector2[0] * cosRadiansAround, vector2[0] * sinRadiansAround, vector2[1] ] dx_ds3 = [ vector3[0] * cosRadiansAround, vector3[0] * sinRadiansAround, vector3[1] ] node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) if useCubicHermiteThroughWall: coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) if useCrossDerivatives: coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) if useCubicHermiteThroughWall: coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 else: # create top apex node node = nodes.createNode(nodeIdentifier, nodetemplateApex) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, [0.0, 0.0, position[1]]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, [0.0, vector2[0], 0.0]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, [-vector2[0], 0.0, 0.0]) if useCubicHermiteThroughWall: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, [0.0, 0.0, vector3[1]]) nodeIdentifier = nodeIdentifier + 1 # create elements elementIdentifier = 1 # now (node offset through wall) varies with number of excluded rows now = 0 if excludeBottomRows == 0: now += 1 # bottom apex node if excludeTopRows == 0: now += 1 # top apex node fullNodeRows = elementsCountUp - 1 if excludeBottomRows > 1: fullNodeRows -= (excludeBottomRows - 1) if excludeTopRows > 1: fullNodeRows -= (excludeTopRows - 1) if fullNodeRows > 0: now += fullNodeRows * elementsCountAround row2NodeOffset = 2 if (excludeBottomRows == 0) else 1 for e3 in range(elementsCountThroughWall): no = e3 * now if excludeBottomRows == 0: # create bottom apex elements, editing eft scale factor identifiers around apex # scale factor identifiers follow convention of offsetting by 100 for each 'version' elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eft1 = eftfactory.createEftShellPoleBottom( va * 100, vb * 100) elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) bni1 = no + 1 bni2 = no + e1 + 2 bni3 = no + (e1 + 1) % elementsCountAround + 2 nodeIdentifiers = [ bni1, bni2, bni3, bni1 + now, bni2 + now, bni3 + now ] element.setNodesByIdentifier(eft1, nodeIdentifiers) # set general linear map coefficients radiansAround = e1 * radiansPerElementAround radiansAroundNext = ( (e1 + 1) % elementsCountAround) * radiansPerElementAround scalefactors = [ -1.0, math.sin(radiansAround), math.cos(radiansAround), radiansPerElementAround, math.sin(radiansAroundNext), math.cos(radiansAroundNext), radiansPerElementAround, math.sin(radiansAround), math.cos(radiansAround), radiansPerElementAround, math.sin(radiansAroundNext), math.cos(radiansAroundNext), radiansPerElementAround ] result = element.setScaleFactors(eft1, scalefactors) elementIdentifier = elementIdentifier + 1 # create regular rows between apexes rowLimit = (elementsCountUp - 2) if excludeBottomRows > 1: rowLimit -= (excludeBottomRows - 1) if excludeTopRows > 1: rowLimit -= (excludeTopRows - 1) for e2 in range(0, rowLimit): for e1 in range(elementsCountAround): element = mesh.createElement(elementIdentifier, elementtemplate) bni11 = no + e2 * elementsCountAround + e1 + row2NodeOffset bni12 = no + e2 * elementsCountAround + ( e1 + 1) % elementsCountAround + row2NodeOffset bni21 = no + ( e2 + 1) * elementsCountAround + e1 + row2NodeOffset bni22 = no + (e2 + 1) * elementsCountAround + ( e1 + 1) % elementsCountAround + row2NodeOffset nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + now, bni12 + now, bni21 + now, bni22 + now ] result = element.setNodesByIdentifier(eft, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 if excludeTopRows == 0: # create top apex elements, editing eft scale factor identifiers around apex # scale factor identifiers follow convention of offsetting by 100 for each 'version' elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eft1 = eftfactory.createEftShellPoleTop(va * 100, vb * 100) elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) bni3 = no + now bni1 = bni3 - elementsCountAround + e1 bni2 = bni3 - elementsCountAround + ( e1 + 1) % elementsCountAround nodeIdentifiers = [ bni1, bni2, bni3, bni1 + now, bni2 + now, bni3 + now ] element.setNodesByIdentifier(eft1, nodeIdentifiers) # set general linear map coefficients radiansAround = math.pi + e1 * radiansPerElementAround radiansAroundNext = math.pi + ( (e1 + 1) % elementsCountAround) * radiansPerElementAround scalefactors = [ -1.0, -math.sin(radiansAround), math.cos(radiansAround), radiansPerElementAround, -math.sin(radiansAroundNext), math.cos(radiansAroundNext), radiansPerElementAround, -math.sin(radiansAround), math.cos(radiansAround), radiansPerElementAround, -math.sin(radiansAroundNext), math.cos(radiansAroundNext), radiansPerElementAround ] result = element.setScaleFactors(eft1, scalefactors) elementIdentifier = elementIdentifier + 1 if False: # (wallThickness < 0.5): # and (wallThicknessRatioApex != 1.0): r = fm.createFieldMagnitude(coordinates) const05 = fm.createFieldConstant([0.5]) d = fm.createFieldSubtract(const05, r) d_r = fm.createFieldDivide(d, r) rRatio = 1.0 - wallThicknessRatioApex rScale = fm.createFieldConstant([rRatio]) zScale = fm.createFieldMultiply(d_r, rScale) one = fm.createFieldConstant([1.0]) one_plus_zScale = fm.createFieldAdd(one, zScale) scale = fm.createFieldConcatenate([one, one, one_plus_zScale]) newCoordinates = fm.createFieldMultiply(coordinates, scale) fieldassignment = coordinates.createFieldassignment(newCoordinates) fieldassignment.assign() fm.endChange() return []
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 generateBaseMesh(region, options): """ Generate the base tricubic Hermite mesh. See also generateMesh(). :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: None """ elementsCountAround = options['Number of elements around'] elementsCountAlong = options['Number of elements along'] elementsCountThroughWall = options['Number of elements through wall'] wallThickness = options['Wall thickness'] useCrossDerivatives = options['Use cross derivatives'] fm = region.getFieldmodule() fm.beginChange() coordinates = findOrCreateFieldCoordinates(fm) 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) 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) mesh = fm.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate.defineField(coordinates, -1, eft) cache = fm.createFieldcache() # create nodes nodeIdentifier = 1 radiansPerElementAround = 2.0 * math.pi / elementsCountAround wallThicknessPerElement = wallThickness / elementsCountThroughWall x = [0.0, 0.0, 0.0] dx_ds1 = [0.0, 0.0, 0.0] dx_ds2 = [0.0, 0.0, 1.0 / elementsCountAlong] dx_ds3 = [0.0, 0.0, 0.0] zero = [0.0, 0.0, 0.0] for n3 in range(elementsCountThroughWall + 1): radius = 0.5 + wallThickness * (n3 / elementsCountThroughWall - 1.0) for n2 in range(elementsCountAlong + 1): x[2] = n2 / elementsCountAlong for n1 in range(elementsCountAround): radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x[0] = radius * cosRadiansAround x[1] = radius * sinRadiansAround dx_ds1[ 0] = radiansPerElementAround * radius * -sinRadiansAround dx_ds1[ 1] = radiansPerElementAround * radius * cosRadiansAround dx_ds3[0] = wallThicknessPerElement * cosRadiansAround dx_ds3[1] = wallThicknessPerElement * sinRadiansAround node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) if useCrossDerivatives: coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 # create elements elementIdentifier = 1 now = (elementsCountAlong + 1) * elementsCountAround for e3 in range(elementsCountThroughWall): for e2 in range(elementsCountAlong): for e1 in range(elementsCountAround): element = mesh.createElement(elementIdentifier, elementtemplate) bni11 = e3 * now + e2 * elementsCountAround + e1 + 1 bni12 = e3 * now + e2 * elementsCountAround + ( e1 + 1) % elementsCountAround + 1 bni21 = e3 * now + (e2 + 1) * elementsCountAround + e1 + 1 bni22 = e3 * now + (e2 + 1) * elementsCountAround + ( e1 + 1) % elementsCountAround + 1 nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + now, bni12 + now, bni21 + now, bni22 + now ] result = element.setNodesByIdentifier(eft, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 fm.endChange()
def createNodesAndElements(region, x, d1, d2, d3, xFlat, d1Flat, d2Flat, xOrgan, d1Organ, d2Organ, organCoordinateFieldName, elementsCountAround, elementsCountAlong, elementsCountThroughWall, annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall, firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives, closedProximalEnd): """ Create nodes and elements for the coordinates and flat coordinates fields. :param x, d1, d2, d3: coordinates and derivatives of coordinates field. :param xFlat, d1Flat, d2Flat, d3Flat: coordinates and derivatives of flat coordinates field. :param xOrgan, d1Organ, d2Organ, d3Organ, organCoordinateFieldName: coordinates, derivatives and name of organ coordinates field. :param elementsCountAround: Number of elements around tube. :param elementsCountAlong: Number of elements along tube. :param elementsCountThroughWall: Number of elements through wall. :param annotationGroupsAround: Annotation groups of elements around. :param annotationGroupsAlong: Annotation groups of elements along. :param annotationGroupsThroughWall: Annotation groups of elements through wall. :param firstNodeIdentifier, firstElementIdentifier: first node and element identifier to use. :param useCubicHermiteThroughWall: use linear when false :param useCrossDerivatives: use cross derivatives when true :return nodeIdentifier, elementIdentifier, allAnnotationGroups """ nodeIdentifier = firstNodeIdentifier elementIdentifier = firstElementIdentifier zero = [0.0, 0.0, 0.0] fm = region.getFieldmodule() fm.beginChange() cache = fm.createFieldcache() # Coordinates field coordinates = findOrCreateFieldCoordinates(fm) 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) if useCrossDerivatives: nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) if useCubicHermiteThroughWall: 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) mesh = fm.findMeshByDimension(3) if useCubicHermiteThroughWall: eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives) else: eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives) eft = eftfactory.createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate.defineField(coordinates, -1, eft) if xFlat: # Flat coordinates field bicubichermitelinear = eftfactory_bicubichermitelinear( mesh, useCrossDerivatives) eftFlat1 = bicubichermitelinear.createEftBasic() eftFlat2 = bicubichermitelinear.createEftOpenTube() flatCoordinates = findOrCreateFieldCoordinates(fm, name="flat coordinates") flatNodetemplate1 = nodes.createNodetemplate() flatNodetemplate1.defineField(flatCoordinates) flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_VALUE, 1) flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D_DS1, 1) flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCrossDerivatives: flatNodetemplate1.setValueNumberOfVersions( flatCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) flatNodetemplate2 = nodes.createNodetemplate() flatNodetemplate2.defineField(flatCoordinates) flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_VALUE, 2) flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D_DS1, 2) flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D_DS2, 2) if useCrossDerivatives: flatNodetemplate2.setValueNumberOfVersions( flatCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 2) flatElementtemplate1 = mesh.createElementtemplate() flatElementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) flatElementtemplate1.defineField(flatCoordinates, -1, eftFlat1) flatElementtemplate2 = mesh.createElementtemplate() flatElementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE) flatElementtemplate2.defineField(flatCoordinates, -1, eftFlat2) if xOrgan: # Organ coordinates field bicubichermitelinear = eftfactory_bicubichermitelinear( mesh, useCrossDerivatives) eftOrgan = bicubichermitelinear.createEftBasic() organCoordinates = findOrCreateFieldCoordinates( fm, name=organCoordinateFieldName) organNodetemplate = nodes.createNodetemplate() organNodetemplate.defineField(organCoordinates) organNodetemplate.setValueNumberOfVersions(organCoordinates, -1, Node.VALUE_LABEL_VALUE, 1) organNodetemplate.setValueNumberOfVersions(organCoordinates, -1, Node.VALUE_LABEL_D_DS1, 1) organNodetemplate.setValueNumberOfVersions(organCoordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCrossDerivatives: organNodetemplate.setValueNumberOfVersions( organCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) organElementtemplate = mesh.createElementtemplate() organElementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) organElementtemplate.defineField(organCoordinates, -1, eftOrgan) # Create nodes # Coordinates field for n in range(len(x)): node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x[n]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1[n]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2[n]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, d3[n]) if useCrossDerivatives: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) # print('NodeIdentifier = ', nodeIdentifier, x[n], d1[n], d2[n]) nodeIdentifier = nodeIdentifier + 1 # Flat coordinates field if xFlat: nodeIdentifier = firstNodeIdentifier for n2 in range(elementsCountAlong + 1): for n3 in range(elementsCountThroughWall + 1): for n1 in range(elementsCountAround): i = n2 * (elementsCountAround + 1) * (elementsCountThroughWall + 1) + (elementsCountAround + 1) * n3 + n1 node = nodes.findNodeByIdentifier(nodeIdentifier) node.merge(flatNodetemplate2 if n1 == 0 else flatNodetemplate1) cache.setNode(node) # print('NodeIdentifier', nodeIdentifier, 'version 1, xList Index =', i+1) flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xFlat[i]) flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1Flat[i]) flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2Flat[i]) if useCrossDerivatives: flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) if n1 == 0: # print('NodeIdentifier', nodeIdentifier, 'version 2, xList Index =', i+elementsCountAround+1) flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_VALUE, 2, xFlat[i + elementsCountAround]) flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS1, 2, d1Flat[i + elementsCountAround]) flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS2, 2, d2Flat[i + elementsCountAround]) if useCrossDerivatives: flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero) nodeIdentifier = nodeIdentifier + 1 # Organ coordinates field if xOrgan: nodeIdentifier = firstNodeIdentifier for n in range(len(xOrgan)): node = nodes.findNodeByIdentifier(nodeIdentifier) node.merge(organNodetemplate) cache.setNode(node) organCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xOrgan[n]) organCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1Organ[n]) organCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2Organ[n]) if useCrossDerivatives: organCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) nodeIdentifier = nodeIdentifier + 1 # create elements elementtemplate3 = mesh.createElementtemplate() elementtemplate3.setElementShapeType(Element.SHAPE_TYPE_CUBE) radiansPerElementAround = math.pi * 2.0 / elementsCountAround allAnnotationGroups = [] if closedProximalEnd: # Create apex for e3 in range(elementsCountThroughWall): for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eft1 = eftfactory.createEftShellPoleBottom(va * 100, vb * 100) elementtemplate3.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate3) bni1 = e3 + 1 bni2 = elementsCountThroughWall + 1 + elementsCountAround * e3 + e1 + 1 bni3 = elementsCountThroughWall + 1 + elementsCountAround * e3 + ( e1 + 1) % elementsCountAround + 1 nodeIdentifiers = [ bni1, bni2, bni3, bni1 + 1, bni2 + elementsCountAround, bni3 + elementsCountAround ] element.setNodesByIdentifier(eft1, nodeIdentifiers) # set general linear map coefficients radiansAround = e1 * radiansPerElementAround radiansAroundNext = ( (e1 + 1) % elementsCountAround) * radiansPerElementAround scalefactors = [ -1.0, math.sin(radiansAround), math.cos(radiansAround), radiansPerElementAround, math.sin(radiansAroundNext), math.cos(radiansAroundNext), radiansPerElementAround, math.sin(radiansAround), math.cos(radiansAround), radiansPerElementAround, math.sin(radiansAroundNext), math.cos(radiansAroundNext), radiansPerElementAround ] result = element.setScaleFactors(eft1, scalefactors) elementIdentifier = elementIdentifier + 1 annotationGroups = annotationGroupsAround[e1] + annotationGroupsAlong[0] + \ annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups( allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: meshGroup = annotationGroup.getMeshGroup(mesh) meshGroup.addElement(element) # Create regular elements now = elementsCountAround * (elementsCountThroughWall + 1) for e2 in range(1 if closedProximalEnd else 0, elementsCountAlong): for e3 in range(elementsCountThroughWall): for e1 in range(elementsCountAround): if closedProximalEnd: bni11 = (e2 - 1) * now + e3 * elementsCountAround + e1 + 1 + ( elementsCountThroughWall + 1) bni12 = (e2-1) * now + e3 * elementsCountAround + (e1 + 1) % elementsCountAround + 1 + \ (elementsCountThroughWall + 1) bni21 = (e2 - 1) * now + ( e3 + 1) * elementsCountAround + e1 + 1 + ( elementsCountThroughWall + 1) bni22 = (e2-1) * now + (e3 + 1) * elementsCountAround + (e1 + 1) % elementsCountAround + 1 + \ (elementsCountThroughWall + 1) else: bni11 = e2 * now + e3 * elementsCountAround + e1 + 1 bni12 = e2 * now + e3 * elementsCountAround + ( e1 + 1) % elementsCountAround + 1 bni21 = e2 * now + (e3 + 1) * elementsCountAround + e1 + 1 bni22 = e2 * now + (e3 + 1) * elementsCountAround + ( e1 + 1) % elementsCountAround + 1 nodeIdentifiers = [ bni11, bni12, bni11 + now, bni12 + now, bni21, bni22, bni21 + now, bni22 + now ] onOpening = e1 > elementsCountAround - 2 element = mesh.createElement(elementIdentifier, elementtemplate) element.setNodesByIdentifier(eft, nodeIdentifiers) if xFlat: element.merge(flatElementtemplate2 if onOpening else flatElementtemplate1) element.setNodesByIdentifier( eftFlat2 if onOpening else eftFlat1, nodeIdentifiers) if xOrgan: element.merge(organElementtemplate) element.setNodesByIdentifier(eftOrgan, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 annotationGroups = annotationGroupsAround[e1] + annotationGroupsAlong[e2] + \ annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups( allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: meshGroup = annotationGroup.getMeshGroup(mesh) meshGroup.addElement(element) fm.endChange() return nodeIdentifier, elementIdentifier, allAnnotationGroups
def generateMesh(region, options): """ :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: None """ elementsCount1 = options['Number of elements 1'] elementsCount2 = options['Number of elements 2'] elementsCount3 = options['Number of elements 3'] elementsCountThroughWall = options['Number of elements through wall'] elementsCountAround = 2 * (elementsCount1 + elementsCount2) holeRadius = options['Hole diameter'] * 0.5 useCrossDerivatives = options['Use cross derivatives'] fm = region.getFieldmodule() fm.beginChange() coordinates = zinc_utils.getOrCreateCoordinateField(fm) 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) 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) mesh = fm.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftBasic() tricubicHermiteBasis = fm.createElementbasis( 3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) # note I'm cheating here by using element-based scale factors # i.e. not shared by neighbouring elements, and I'm also using the same scale # factors for the bottom and top of each element eftOuter1 = mesh.createElementfieldtemplate(tricubicHermiteBasis) eftOuter2 = mesh.createElementfieldtemplate(tricubicHermiteBasis) i = 0 for eftOuter in [eftOuter1, eftOuter2]: i += 1 eftOuter.setNumberOfLocalScaleFactors(10) eft.setScaleFactorType( 1, Elementfieldtemplate.SCALE_FACTOR_TYPE_GLOBAL_GENERAL) # GRC: allow scale factor identifier for global -1 to be prescribed eft.setScaleFactorIdentifier(1, 1) # Global scale factor 4 to subtract lateral derivative term from cross derivative to fix edges eft.setScaleFactorType( 2, Elementfieldtemplate.SCALE_FACTOR_TYPE_GLOBAL_GENERAL) eft.setScaleFactorIdentifier(2, 2) nonCrossDerivativesNodes = [0, 1, 4, 5 ] if useCrossDerivatives else range(8) for n in nonCrossDerivativesNodes: eftOuter.setFunctionNumberOfTerms(n * 8 + 4, 0) eftOuter.setFunctionNumberOfTerms(n * 8 + 6, 0) eftOuter.setFunctionNumberOfTerms(n * 8 + 7, 0) eftOuter.setFunctionNumberOfTerms(n * 8 + 8, 0) # nodes 1,2,5,6: general map dxi1, dsxi2 from ds1, ds2 for n in [0, 1, 4, 5]: ln = n + 1 sfo = (n % 2) * 4 + 2 eftOuter.setFunctionNumberOfTerms(n * 8 + 2, 2) eftOuter.setTermNodeParameter(n * 8 + 2, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftOuter.setTermScaling(n * 8 + 2, 1, [1 + sfo]) eftOuter.setTermNodeParameter(n * 8 + 2, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftOuter.setTermScaling(n * 8 + 2, 2, [2 + sfo]) eftOuter.setFunctionNumberOfTerms(n * 8 + 3, 2) eftOuter.setTermNodeParameter(n * 8 + 3, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftOuter.setTermScaling(n * 8 + 3, 1, [3 + sfo]) eftOuter.setTermNodeParameter(n * 8 + 3, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftOuter.setTermScaling(n * 8 + 3, 2, [4 + sfo]) # map d2_dxi1dxi2 to subtract corner angle terms to fix edge continuity eftOuter.setFunctionNumberOfTerms(n * 8 + 4, 1) if i == 1: eftOuter.setTermNodeParameter(n * 8 + 4, 1, ln, Node.VALUE_LABEL_D_DS1, 1) if (n % 2) == 0: eftOuter.setTermScaling(n * 8 + 4, 1, [1, 2, 3 + sfo]) else: eftOuter.setTermScaling(n * 8 + 4, 1, [2, 3 + sfo]) else: eftOuter.setTermNodeParameter(n * 8 + 4, 1, ln, Node.VALUE_LABEL_D_DS2, 1) if (n % 2) == 0: eftOuter.setTermScaling(n * 8 + 4, 1, [1, 2, 4 + sfo]) else: eftOuter.setTermScaling(n * 8 + 4, 1, [2, 4 + sfo]) elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate.defineField(coordinates, -1, eft) elementtemplateOuter1 = mesh.createElementtemplate() elementtemplateOuter1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplateOuter1.defineField(coordinates, -1, eftOuter1) elementtemplateOuter2 = mesh.createElementtemplate() elementtemplateOuter2.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplateOuter2.defineField(coordinates, -1, eftOuter2) cache = fm.createFieldcache() # create nodes radiansPerElementAround = 2.0 * math.pi / elementsCountAround wallThicknessMin = (0.5 - holeRadius) wallThicknessMax = math.sqrt(0.5) - holeRadius wallThicknessPerElement = wallThicknessMin / elementsCountThroughWall radius = holeRadius inner_x = [] inner_d1 = [] inner_d2 = [] startRadians = math.pi * (-0.5 - elementsCount1 / elementsCountAround) for n1 in range(elementsCountAround): radiansAround = startRadians + n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) inner_x.append( (radius * cosRadiansAround, radius * sinRadiansAround)) inner_d1.append( (radiansPerElementAround * radius * -sinRadiansAround, radiansPerElementAround * radius * cosRadiansAround)) #inner_d2.append((wallThicknessPerElement*cosRadiansAround, wallThicknessPerElement*sinRadiansAround)) inner_d2.append((wallThicknessMin * cosRadiansAround, wallThicknessMin * sinRadiansAround)) outer_x = [] outer_d1 = [] outer_d2 = [] mag = math.sqrt(elementsCount1 * elementsCount1 + elementsCount2 * elementsCount2) outer_dx1_ds1 = 1.0 / elementsCount1 outer_dx2_ds2 = 1.0 / elementsCount2 #cornerScale1 = 1.0 / max(elementsCount1 + 1, elementsCount2 + 1) #cornerScale1 = 1.0 / min(2, max(elementsCount1, elementsCount2)) cornerScale1 = 1.0 / max(elementsCount1 + elementsCountThroughWall, elementsCount2 + elementsCountThroughWall) sqrt05 = math.sqrt(0.5) wallThicknessMax = sqrt05 - holeRadius for n in range(elementsCount1): x = -0.5 + n / elementsCount1 outer_x.append((x, -0.5)) if n == 0: rx = x / sqrt05 ry = -0.5 / sqrt05 scale2 = wallThicknessMax outer_d1.append((-ry * cornerScale1, rx * cornerScale1)) outer_d2.append((rx * scale2, ry * scale2)) else: scale2 = wallThicknessMin outer_d1.append((outer_dx1_ds1, 0.0)) outer_d2.append((0.0, -scale2)) for n in range(elementsCount2): y = -0.5 + n / elementsCount2 outer_x.append((0.5, y)) if n == 0: rx = 0.5 / sqrt05 ry = y / sqrt05 scale2 = wallThicknessMax outer_d1.append((-ry * cornerScale1, rx * cornerScale1)) outer_d2.append((rx * scale2, ry * scale2)) else: scale2 = wallThicknessMin outer_d1.append((0.0, outer_dx2_ds2)) outer_d2.append((scale2, 0.0)) for n in range(elementsCount1): x = 0.5 - n / elementsCount1 outer_x.append((x, 0.5)) if n == 0: rx = x / sqrt05 ry = 0.5 / sqrt05 scale2 = wallThicknessMax outer_d1.append((-ry * cornerScale1, rx * cornerScale1)) outer_d2.append((rx * scale2, ry * scale2)) else: scale2 = wallThicknessMin outer_d1.append((-outer_dx1_ds1, 0.0)) outer_d2.append((0.0, scale2)) for n in range(elementsCount2): y = 0.5 - n / elementsCount2 outer_x.append((-0.5, y)) if n == 0: rx = -0.5 / sqrt05 ry = y / sqrt05 scale2 = wallThicknessMax outer_d1.append((-ry * cornerScale1, rx * cornerScale1)) outer_d2.append((rx * scale2, ry * scale2)) else: scale2 = wallThicknessMin outer_d1.append((0.0, -outer_dx2_ds2)) outer_d2.append((-scale2, 0.0)) nodeIdentifier = 1 x = [0.0, 0.0, 0.0] dx_ds1 = [0.0, 0.0, 0.0] dx_ds2 = [0.0, 0.0, 0.0] dx_ds3 = [0.0, 0.0, 1.0 / elementsCount3] outer_dx_ds1 = [1.0 / elementsCount1, 0.0, 0.0] outer_dx_ds2 = [0.0, 1.0 / elementsCount2, 0.0] outer_dx_ds3 = [0.0, 0.0, 1.0 / elementsCount3] zero = [0.0, 0.0, 0.0] for n3 in range(elementsCount3 + 1): x[2] = n3 / elementsCount3 # outer nodes for n1 in range(elementsCountAround): x[0] = outer_x[n1][0] x[1] = outer_x[n1][1] node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, outer_dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, outer_dx_ds2) #coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, [ outer_d1[n1][0], outer_d1[n1][1], 0.0 ]) #coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, [ outer_d2[n1][0], outer_d2[n1][1], 0.0 ]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, outer_dx_ds3) if useCrossDerivatives: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 # inner nodes for n2 in range(elementsCountThroughWall): xir = (n2 + 1) / elementsCountThroughWall xi = 1.0 - xir for n1 in range(elementsCountAround): node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) v = interpolateCubicHermite(inner_x[n1], inner_d2[n1], outer_x[n1], outer_d2[n1], xi) x[0] = v[0] x[1] = v[1] dx_ds1[0] = xir * inner_d1[n1][0] + xi * outer_d1[n1][0] dx_ds1[1] = xir * inner_d1[n1][1] + xi * outer_d1[n1][1] d2 = interpolateCubicHermiteDerivative( inner_x[n1], inner_d2[n1], outer_x[n1], outer_d2[n1], xi) dx_ds2[0] = -d2[ 0] / elementsCountThroughWall # *wallThicknessPerElement dx_ds2[1] = -d2[ 1] / elementsCountThroughWall # *wallThicknessPerElement coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) if useCrossDerivatives: coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 # create elements elementIdentifier = 1 no3 = (elementsCountThroughWall + 1) * elementsCountAround for e3 in range(elementsCount3): # first row general maps ds1, ds2 to dxi1, dxi2 for e1 in range(elementsCountAround): en = (e1 + 1) % elementsCountAround onX = (e1 % (elementsCount1 + elementsCount2)) < elementsCount1 elementtemplateOuter = elementtemplateOuter1 if onX else elementtemplateOuter2 eftOuter = eftOuter1 if onX else eftOuter2 element = mesh.createElement(elementIdentifier, elementtemplateOuter) bni11 = e3 * no3 + e1 + 1 bni12 = e3 * no3 + en + 1 bni21 = e3 * no3 + elementsCountAround + e1 + 1 bni22 = e3 * no3 + elementsCountAround + en + 1 nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + no3, bni12 + no3, bni21 + no3, bni22 + no3 ] result = element.setNodesByIdentifier(eftOuter, nodeIdentifiers) rev = e1 >= (elementsCount1 + elementsCount2) one = -1.0 if rev else 1.0 vx = one if onX else 0.0 vy = 0.0 if onX else one scaleFactors = [ -1.0, 4.0, vx, vy, -outer_d2[e1][0] / outer_dx_ds1[0] / elementsCountThroughWall, -outer_d2[e1][1] / outer_dx_ds2[1] / elementsCountThroughWall, vx, vy, -outer_d2[en][0] / outer_dx_ds1[0] / elementsCountThroughWall, -outer_d2[en][1] / outer_dx_ds2[1] / elementsCountThroughWall ] element.setScaleFactors(eftOuter, scaleFactors) elementIdentifier = elementIdentifier + 1 # remaining rows for e2 in range(1, elementsCountThroughWall): for e1 in range(elementsCountAround): element = mesh.createElement(elementIdentifier, elementtemplate) bni11 = e3 * no3 + e2 * elementsCountAround + e1 + 1 bni12 = e3 * no3 + e2 * elementsCountAround + ( e1 + 1) % elementsCountAround + 1 bni21 = e3 * no3 + (e2 + 1) * elementsCountAround + e1 + 1 bni22 = e3 * no3 + (e2 + 1) * elementsCountAround + ( e1 + 1) % elementsCountAround + 1 nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + no3, bni12 + no3, bni21 + no3, bni22 + no3 ] result = element.setNodesByIdentifier(eft, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 fm.endChange()
def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, startNodeIdentifier = 1, startElementIdentifier = 1, vesselMeshGroups = None): ''' :param vesselMeshGroups: List (over number of vessels) of list of mesh groups to add vessel elements to. :return: nextNodeIdentifier, nextElementIdentifier, Ostium points tuple (ox[n3][n1][c], od1[n3][n1][c], od2[n3][n1][c], od3[n3][n1][c], oNodeId[n3][n1], oPositions). ''' vesselsCount = options['Number of vessels'] elementsCountAroundOstium = options['Number of elements around ostium'] elementsCountAcross = options['Number of elements across common'] elementsCountsAroundVessels, elementsCountAroundMid = getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCountAcross, vesselsCount) elementsCountAroundEnd = (elementsCountAroundOstium - 2*elementsCountAroundMid)//2 #print('\nvesselsCount', vesselsCount, 'elementsCountsAroundOstium', elementsCountAroundOstium, 'elementsCountAcross', elementsCountAcross) #print('--> elementsCountsAroundVessels', elementsCountsAroundVessels, 'elementsCountAroundMid', elementsCountAroundMid) elementsCountAlong = options['Number of elements along'] elementsCountThroughWall = options['Number of elements through wall'] unitScale = options['Unit scale'] isOutlet = options['Outlet'] ostiumRadius = 0.5*unitScale*options['Ostium diameter'] ostiumLength = unitScale*options['Ostium length'] ostiumWallThickness = unitScale*options['Ostium wall thickness'] interVesselHeight = unitScale*options['Ostium inter-vessel height'] interVesselDistance = unitScale*options['Ostium inter-vessel distance'] if (vesselsCount > 1) else 0.0 halfInterVesselDistance = 0.5*interVesselDistance useCubicHermiteThroughOstiumWall = not(options['Use linear through ostium wall']) vesselEndDerivative = ostiumLength*options['Vessel end length factor']/elementsCountAlong vesselInnerRadius = 0.5*unitScale*options['Vessel inner diameter'] vesselWallThickness = unitScale*options['Vessel wall thickness'] vesselOuterRadius = vesselInnerRadius + vesselWallThickness vesselAngle1Radians = math.radians(options['Vessel angle 1 degrees']) vesselAngle1SpreadRadians = math.radians(options['Vessel angle 1 spread degrees']) vesselAngle2Radians = math.radians(options['Vessel angle 2 degrees']) useCubicHermiteThroughVesselWall = not(options['Use linear through vessel wall']) useCrossDerivatives = False # options['Use cross derivatives'] # not implemented fm = region.getFieldmodule() fm.beginChange() coordinates = findOrCreateFieldCoordinates(fm) cache = fm.createFieldcache() # track points in shape of ostium # get directions in plane of surface at centre: cx, cd1, cd2 = trackSurface.evaluateCoordinates(centrePosition, True) trackDirection1, trackDirection2, centreNormal = calculate_surface_axes(cd1, cd2, axis1) trackDirection2reverse = [ -d for d in trackDirection2 ] halfCircumference = math.pi*ostiumRadius circumference = 2.0*halfCircumference distance = 0.0 elementLengthAroundOstiumMid = 0.0 vesselsSpanAll = interVesselDistance*(vesselsCount - 1) vesselsSpanMid = interVesselDistance*(vesselsCount - 2) if vesselsCount == 1: elementLengthAroundOstiumEnd = circumference/elementsCountAroundOstium vesselOstiumPositions = [ centrePosition ] ocx = [ cx ] ocd1 = [ trackDirection1 ] ocd2 = [ trackDirection2 ] ocd3 = [ centreNormal ] else: elementLengthAroundOstiumEnd = (circumference + 2.0*interVesselDistance)/(elementsCountAroundOstium - 2*elementsCountAroundMid) if elementsCountAroundMid > 0: elementLengthAroundOstiumMid = interVesselDistance*(vesselsCount - 2)/elementsCountAroundMid vesselOstiumPositions = [] ocx = [] ocd1 = [] ocd2 = [] ocd3 = [] for v in range(vesselsCount): vesselOstiumPositions.append(trackSurface.trackVector(centrePosition, trackDirection1, (v/(vesselsCount - 1) - 0.5)*vesselsSpanAll)) x, d1, d2 = trackSurface.evaluateCoordinates(vesselOstiumPositions[-1], -1) d1, d2, d3 = calculate_surface_axes(d1, d2, trackDirection1) ocx .append(x) ocd1.append(d1) ocd2.append(d2) ocd3.append(d3) # coordinates around ostium ox = [ [], [] ] od1 = [ [], [] ] od2 = [ [], [] ] od3 = [ [], [] ] oPositions = [] for n1 in range(elementsCountAroundOstium): elementLength = elementLengthAroundOstiumEnd if distance <= (vesselsSpanMid + halfInterVesselDistance): position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5*vesselsSpanMid - distance) sideDirection = trackDirection2reverse if n1 < elementsCountAroundMid: elementLength = elementLengthAroundOstiumMid elif distance < (vesselsSpanMid + halfInterVesselDistance + halfCircumference): position = vesselOstiumPositions[0] angleRadians = (distance - (vesselsSpanMid + halfInterVesselDistance))/ostiumRadius w1 = -math.sin(angleRadians) w2 = -math.cos(angleRadians) sideDirection = [ (w1*trackDirection1[c] + w2*trackDirection2[c]) for c in range(3) ] elif distance < (2.0*vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance): position = trackSurface.trackVector(centrePosition, trackDirection1, distance - (1.5*vesselsSpanMid + interVesselDistance + halfCircumference)) sideDirection = trackDirection2 if 0 <= (n1 - elementsCountAroundEnd - elementsCountAroundMid) < elementsCountAroundMid: elementLength = elementLengthAroundOstiumMid elif distance < (2.0*vesselsSpanMid + halfInterVesselDistance + circumference + interVesselDistance): position = vesselOstiumPositions[-1] angleRadians = (distance - (2.0*vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance))/ostiumRadius w1 = math.sin(angleRadians) w2 = math.cos(angleRadians) sideDirection = [ (w1*trackDirection1[c] + w2*trackDirection2[c]) for c in range(3) ] else: position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5*vesselsSpanMid + (circumference + 2.0*(vesselsSpanMid + interVesselDistance)) - distance) sideDirection = trackDirection2reverse position = trackSurface.trackVector(position, sideDirection, ostiumRadius) oPositions.append(position) px, d1, d2 = trackSurface.evaluateCoordinates(position, True) pd2, pd1, pd3 = calculate_surface_axes(d1, d2, sideDirection) # get outer coordinates opx = px opd1 = vector.setMagnitude([ -d for d in pd1 ], elementLengthAroundOstiumEnd) opd2 = vector.setMagnitude(pd2, elementLengthAroundOstiumEnd) # smoothed later opd3 = vector.setMagnitude(pd3, ostiumWallThickness) # set inner and outer coordinates (use copy to avoid references to same list later) ox [0].append([ (opx[c] - opd3[c]) for c in range(3) ]) od1[0].append(copy.copy(opd1)) od2[0].append(copy.copy(opd2)) ox [1].append(opx) od1[1].append(opd1) od2[1].append(opd2) if useCubicHermiteThroughOstiumWall: od3[0].append(copy.copy(opd3)) od3[1].append(opd3) distance += elementLength for n3 in range(2): od1[n3] = interp.smoothCubicHermiteDerivativesLoop(ox[n3], od1[n3], fixAllDirections = True) xx = [] xd1 = [] xd2 = [] xd3 = [] # coordinates across common ostium, between vessels nodesCountFreeEnd = elementsCountsAroundVessels[0] + 1 - elementsCountAcross oinc = 0 if (vesselsCount <= 2) else elementsCountAroundMid//(vesselsCount - 2) for iv in range(vesselsCount - 1): xx .append([ None, None ]) xd1.append([ None, None ]) xd2.append([ None, None ]) xd3.append([ None, None ]) oa = elementsCountAroundMid - iv*oinc ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc nx = [ ox[1][oa], ox[1][ob] ] nd1 = [ [ -d for d in od1[1][oa] ], od1[1][ob] ] nd2 = [ [ -d for d in od2[1][oa] ], od2[1][ob] ] if elementsCountAcross > 1: # add centre point, displaced by interVesselHeight if vesselsCount == 2: position = centrePosition else: position = trackSurface.trackVector(centrePosition, trackDirection1, (iv/(vesselsCount - 2) - 0.5)*vesselsSpanMid) mx, d1, d2 = trackSurface.evaluateCoordinates(position, derivatives = True) md1, md2, md3 = calculate_surface_axes(d1, d2, trackDirection1) nx .insert(1, [ (mx[c] + interVesselHeight*md3[c]) for c in range(3) ]) nd1.insert(1, vector.setMagnitude(md1, elementLengthAroundOstiumMid if (0 < iv < (vesselsCount - 2)) else elementLengthAroundOstiumEnd)) nd2.insert(1, vector.setMagnitude(md2, ostiumRadius)) nd2 = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixAllDirections = True) px, pd2, pe, pxi = interp.sampleCubicHermiteCurves(nx, nd2, elementsCountAcross)[0:4] pd1 = interp.interpolateSampleLinear(nd1, pe, pxi) pd3 = [ vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), ostiumWallThickness) for n2 in range(elementsCountAcross + 1) ] lx = [ ([ (px[n2][c] - pd3[n2][c]) for c in range(3) ]) for n2 in range(elementsCountAcross + 1) ] ld2 = interp.smoothCubicHermiteDerivativesLine(lx, pd2, fixAllDirections = True) xx [iv][0] = lx [1:elementsCountAcross] xd1[iv][0] = copy.deepcopy(pd1[1:elementsCountAcross]) # to be smoothed later xd2[iv][0] = ld2[1:elementsCountAcross] xx [iv][1] = px [1:elementsCountAcross] xd1[iv][1] = pd1[1:elementsCountAcross] # to be smoothed later xd2[iv][1] = pd2[1:elementsCountAcross] if useCubicHermiteThroughOstiumWall: xd3[iv][0] = copy.deepcopy(pd3[1:elementsCountAcross]) xd3[iv][1] = pd3[1:elementsCountAcross] # set smoothed d2 on ostium circumference od2[0][oa] = [ -d for d in ld2[0] ] od2[1][oa] = [ -d for d in pd2[0] ] od2[0][ob] = ld2[-1] od2[1][ob] = pd2[-1] # get positions of vessel end centres and rings vcx = [] vcd1 = [] vcd2 = [] vcd3 = [] vox = [] vod1 = [] vod2 = [] vod3 = [] for v in range(vesselsCount): elementsCountAroundVessel = elementsCountsAroundVessels[v] radiansPerElementVessel = 2.0*math.pi/elementsCountAroundVessel useVesselAngleRadians = vesselAngle1Radians if vesselsCount > 1: useVesselAngleRadians += (v/(vesselsCount - 1) - 0.5)*vesselAngle1SpreadRadians vx, vd1, vd2, vd3 = getCircleProjectionAxes(ocx[v], ocd1[v], ocd2[v], ocd3[v], ostiumLength, useVesselAngleRadians, vesselAngle2Radians) vd1 = [ vesselOuterRadius*d for d in vd1 ] vd2 = [ -vesselOuterRadius*d for d in vd2 ] vd3 = [ -vesselEndDerivative*d for d in vd3 ] vcx.append(vx) vcd1.append(vd1) vcd2.append(vd2) vcd3.append(vd3) vox.append([]) vod1.append([]) vod2.append([]) vod3.append([]) for n3 in range(2): radius = vesselInnerRadius if (n3 == 0) else vesselOuterRadius vAxis1 = vector.setMagnitude(vd1, radius) vAxis2 = vector.setMagnitude(vd2, radius) if vesselsCount == 1: startRadians = 0.5*math.pi else: startRadians = 0.5*radiansPerElementVessel*elementsCountAcross if v == (vesselsCount - 1): startRadians -= math.pi px, pd1 = createCirclePoints(vx, vAxis1, vAxis2, elementsCountAroundVessel, startRadians) vox [-1].append(px) vod1[-1].append(pd1) vod2[-1].append([ vd3 ]*elementsCountAroundVessel) if useCubicHermiteThroughVesselWall: vod3[-1].append([ vector.setMagnitude(vector.crossproduct3(d1, vd3), vesselWallThickness) for d1 in pd1 ]) # calculate common ostium vessel node derivatives map mvPointsx = [ None ]*vesselsCount mvPointsd1 = [ None ]*vesselsCount mvPointsd2 = [ None ]*vesselsCount mvPointsd3 = [ None ]*vesselsCount mvDerivativesMap = [ None ]*vesselsCount mvMeanCount = [ None ]*vesselsCount # stores 1 if first reference to common point between vessels, 2 if second. Otherwise 0. for v in range(vesselsCount): if vesselsCount == 1: mvPointsx[v], mvPointsd1[v], mvPointsd2[v], mvPointsd3[v], mvDerivativesMap[v] = \ ox, od1, od2, od3 if useCubicHermiteThroughOstiumWall else None, None mvMeanCount[v] = [ 0 ]*elementsCountsAroundVessels[v] else: iv = max(0, v - 1) oa = elementsCountAroundMid - iv*oinc ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc mvPointsx [v] = [] mvPointsd1[v] = [] mvPointsd2[v] = [] mvPointsd3[v] = [] if useCubicHermiteThroughOstiumWall else None mvDerivativesMap[v] = [] for n3 in range(2): mvPointsd1[v].append([]) mvPointsd2[v].append([]) mvPointsx [v].append([]) if useCubicHermiteThroughOstiumWall: mvPointsd3[v].append([]) mvDerivativesMap[v].append([]) if v == 0: # first end vessel mvPointsd1[v][n3] += od1[n3][oa:ob + 1] mvPointsd2[v][n3] += od2[n3][oa:ob + 1] mvPointsx [v][n3] += ox [n3][oa:ob + 1] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][oa:ob + 1] mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) for i in range(nodesCountFreeEnd - 2): mvDerivativesMap[v][n3].append( ( None, None, None ) ) mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) mvPointsx [v][n3] += reversed(xx [iv][n3]) mvPointsd1[v][n3] += reversed(xd1[iv][n3]) mvPointsd2[v][n3] += reversed(xd2[iv][n3]) if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += reversed(xd3[iv][n3]) for i in range(elementsCountAcross - 1): mvDerivativesMap[v][n3].append( ( (0, -1, 0), (1, 0, 0), None ) ) if n3 == 0: mvMeanCount[v] = [ 1 ] + [ 0 ]*(nodesCountFreeEnd - 2) + [ 1 ]*elementsCountAcross elif v < (vesselsCount - 1): # middle vessels # left: mvPointsx [v][n3] += ox [n3][oa - oinc:oa + 1] mvPointsd1[v][n3] += od1[n3][oa - oinc:oa + 1] mvPointsd2[v][n3] += od2[n3][oa - oinc:oa + 1] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][oa - oinc:oa + 1] mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) for i in range(oinc - 1): mvDerivativesMap[v][n3].append( ( None, None, None ) ) mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) # across mvPointsx [v][n3] += xx [iv][n3] mvPointsd1[v][n3] += xd1[iv][n3] mvPointsd2[v][n3] += xd2[iv][n3] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += xd3[iv][n3] for i in range(elementsCountAcross - 1): mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 0, 0), None ) ) # right mvPointsx [v][n3] += ox [n3][ob:ob + oinc + 1] mvPointsd1[v][n3] += od1[n3][ob:ob + oinc + 1] mvPointsd2[v][n3] += od2[n3][ob:ob + oinc + 1] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][ob:ob + oinc + 1] mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) for i in range(oinc - 1): mvDerivativesMap[v][n3].append( ( None, None, None ) ) mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) # across reverse mvPointsx [v][n3] += reversed(xx [iv + 1][n3]) mvPointsd1[v][n3] += reversed(xd1[iv + 1][n3]) mvPointsd2[v][n3] += reversed(xd2[iv + 1][n3]) if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += reversed(xd3[iv + 1][n3]) for i in range(elementsCountAcross - 1): mvDerivativesMap[v][n3].append( ( (0, -1, 0), (1, 0, 0), None ) ) if n3 == 0: mvMeanCount[v] = [ 1 ] + [ 0 ]*(oinc - 1) + [ 2 ]*(elementsCountAcross + 1) + [ 0 ]*(oinc - 1) + [ 1 ]*elementsCountAcross else: # last end vessel mvPointsx [v][n3] += ox [n3][ob:] + [ ox [n3][0] ] mvPointsd1[v][n3] += od1[n3][ob:] + [ od1[n3][0] ] mvPointsd2[v][n3] += od2[n3][ob:] + [ od2[n3][0] ] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][ob:] + [ od3[n3][0] ] mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) for i in range(nodesCountFreeEnd - 2): mvDerivativesMap[v][n3].append( ( None, None, None ) ) mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) mvPointsx [v][n3] += xx [iv][n3] mvPointsd1[v][n3] += xd1[iv][n3] mvPointsd2[v][n3] += xd2[iv][n3] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += xd3[iv][n3] for i in range(elementsCountAcross - 1): mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 0, 0), None ) ) if n3 == 0: mvMeanCount[v] = [ 2 ] + [ 0 ]*(nodesCountFreeEnd - 2) + [ 2 ]*elementsCountAcross # calculate derivative 2 around free sides of inlets to fit vessel derivatives for v in range(vesselsCount): for n3 in range(2): #print('v',v,'n3',n3,'elementsAround',elementsCountsAroundVessels[v]) #print('mvPointsx [v][n3]', mvPointsx [v][n3]) #print('mvPointsd1[v][n3]', mvPointsd1[v][n3]) #print('mvPointsd2[v][n3]', mvPointsd2[v][n3]) #print('mvDerivativesMap[v][n3]', mvDerivativesMap[v][n3]) for n1 in range(elementsCountsAroundVessels[v]): d2Map = mvDerivativesMap[v][n3][n1][1] if (mvDerivativesMap[v] and mvDerivativesMap[v][n3][n1]) else None sf1 = d2Map[0] if d2Map else 0.0 sf2 = d2Map[1] if d2Map else 1.0 nx = [ vox[v][n3][n1], mvPointsx[v][n3][n1] ] nd2 = [ [ d*elementsCountAlong for d in vod2[v][n3][n1] ], [ (sf1*mvPointsd1[v][n3][n1][c] + sf2*mvPointsd2[v][n3][n1][c]) for c in range(3) ] ] nd2f = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixStartDerivative = True, fixEndDirection = True) ndf = [ d/elementsCountAlong for d in nd2f[1] ] # assign components to set original values: if sf1 == 0: for c in range(3): mvPointsd2[v][n3][n1][c] = sf2*ndf[c] elif sf2 == 0: if mvMeanCount[v][n1] < 2: for c in range(3): mvPointsd1[v][n3][n1][c] = sf1*ndf[c] else: # take mean of values from this and last vessel for c in range(3): mvPointsd1[v][n3][n1][c] = 0.5*(mvPointsd1[v][n3][n1][c] + sf1*ndf[c]) else: #print('v', v, 'n3', n3, 'n1', n1, ':', vector.magnitude(ndf), 'vs.', vector.magnitude(nd2[1]), 'd2Map', d2Map) pass if isOutlet: # reverse directions of d1 and d2 on vessels and ostium base for c in range(3): for n3 in range(2): for n1 in range(elementsCountAroundOstium): od1[n3][n1][c] = -od1[n3][n1][c] od2[n3][n1][c] = -od2[n3][n1][c] for iv in range(vesselsCount - 1): for n1 in range(elementsCountAcross - 1): xd1[iv][n3][n1][c] = -xd1[iv][n3][n1][c] xd2[iv][n3][n1][c] = -xd2[iv][n3][n1][c] for v in range(vesselsCount): for n1 in range(elementsCountsAroundVessels[v]): vod1[v][n3][n1][c] = -vod1[v][n3][n1][c] # d2 is referenced all around, so only change once per vessel for v in range(vesselsCount): vod2[v][0][0][c] = -vod2[v][0][0][c] ############## # Create nodes ############## nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) nodetemplateLinearS3 = nodes.createNodetemplate() nodetemplateLinearS3.defineField(coordinates) nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) nodeIdentifier = startNodeIdentifier oNodeId = [] for n3 in range(2): oNodeId.append([]) for n1 in range(elementsCountAroundOstium): node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, ox [n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, od1[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, od2[n3][n1]) if useCubicHermiteThroughOstiumWall: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, od3[n3][n1]) oNodeId[n3].append(nodeIdentifier) nodeIdentifier += 1 xNodeId = [] for iv in range(vesselsCount - 1): xNodeId.append([]) for n3 in range(2): xNodeId[iv].append([]) for n2 in range(elementsCountAcross - 1): node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xx [iv][n3][n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, xd1[iv][n3][n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, xd2[iv][n3][n2]) if useCubicHermiteThroughOstiumWall: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, xd3[iv][n3][n2]) xNodeId[iv][n3].append(nodeIdentifier) nodeIdentifier += 1 #for v in range(vesselsCount): # node = nodes.createNode(nodeIdentifier, nodetemplate) # cache.setNode(node) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, vcx [v]) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, vcd1[v]) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, vcd2[v]) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, vcd3[v]) # nodeIdentifier += 1 # for n3 in range(2): # for n1 in range(elementsCountsAroundVessels[v]): # node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughVesselWall else nodetemplateLinearS3) # cache.setNode(node) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, vox [v][n3][n1]) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, vod1[v][n3][n1]) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, vod2[v][n3][n1]) # if useCubicHermiteThroughVesselWall: # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, vod3[v][n3][n1]) # #vNodeId.append(nodeIdentifier) # nodeIdentifier += 1 # get identifiers of nodes around each vessel at ostium end mvNodeId = [ None ]*vesselsCount for v in range(vesselsCount): if vesselsCount == 1: mvNodeId[v] = oNodeId else: iv = max(0, v - 1) mvNodeId[v] = [ None, None ] oa = elementsCountAroundMid - iv*oinc ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc for n3 in range(2): if v == 0: # first end vessel mvNodeId[v][n3] = oNodeId[n3][oa:ob + 1] + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) elif v == (vesselsCount - 1): # last end vessels mvNodeId[v][n3] = oNodeId[n3][ob:] + [ oNodeId[n3][0] ] + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) else: # mid vessels mvNodeId[v][n3] = oNodeId[n3][oa - oinc:oa + 1] + xNodeId[iv][n3] + oNodeId[n3][ob:ob + oinc + 1] + list(reversed(xNodeId[iv + 1][n3])) ################# # Create elementss ################# mesh = fm.findMeshByDimension(3) elementIdentifier = startElementIdentifier tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) #tricubicHermiteBasis = fm.createElementbasis(3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) #eft = tricubichermite.createEftBasic() #elementtemplate = mesh.createElementtemplate() #elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) #elementtemplate.defineField(coordinates, -1, eft) #elementtemplateX = mesh.createElementtemplate() #elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) for v in range(vesselsCount): if isOutlet: startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap = \ mvPointsx[v], mvPointsd1[v], mvPointsd2[v], mvPointsd3[v], mvNodeId[v], mvDerivativesMap[v] endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap = \ vox[v], vod1[v], vod2[v], vod3[v] if useCubicHermiteThroughVesselWall else None, None, None # reverse order of nodes around: for px in [ startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, \ endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap ]: if px: for n3 in range(2): px[n3] = [ px[n3][0] ] + px[n3][len(px[n3]) - 1:0:-1] if vesselsCount > 1: # must switch in and out xi1 maps around corners in startDerivativesMap for n3 in range(2): for n1 in range(elementsCountsAroundVessels[v]): derivativesMap = startDerivativesMap[n3][n1] if len(derivativesMap) == 4: startDerivativesMap[n3][n1] = derivativesMap[3], derivativesMap[1], derivativesMap[2], derivativesMap[0] else: startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap = \ vox[v], vod1[v], vod2[v], vod3[v] if useCubicHermiteThroughVesselWall else None, None, None endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap = \ mvPointsx[v], mvPointsd1[v], mvPointsd2[v], mvPointsd3[v], mvNodeId[v], mvDerivativesMap[v] #print('endPointsx ', endPointsx ) #print('endPointsd1', endPointsd1) #print('endPointsd2', endPointsd2) #print('endPointsd3', endPointsd3) #print('endNodeId', endNodeId) #print('endDerivativesMap', endDerivativesMap) nodeIdentifier, elementIdentifier = createAnnulusMesh3d( nodes, mesh, nodeIdentifier, elementIdentifier, startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap, forceMidLinearXi3 = not useCubicHermiteThroughVesselWall, elementsCountRadial = elementsCountAlong, meshGroups = vesselMeshGroups[v] if vesselMeshGroups else []) fm.endChange() return nodeIdentifier, elementIdentifier, (ox, od1, od2, od3, oNodeId, oPositions)
def createNodesAndElements(region, x, d1, d2, d3, xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture, elementsCountAround, elementsCountAlong, elementsCountThroughWall, annotationGroups, annotationArray, firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives): """ Create nodes and elements for the coordinates, flat coordinates, and texture coordinates field. :param x, d1, d2, d3: coordinates and derivatives of coordinates field. :param xFlat, d1Flat, d2Flat, d3Flat: coordinates and derivatives of flat coordinates field. :param xTexture, d1Texture, d2Texture, d3Texture: coordinates and derivatives of texture coordinates field. :param elementsCountAround: Number of elements around tube. :param elementsCountAlong: Number of elements along tube. :param elementsCountThroughWall: Number of elements through wall. :param annotationGroups: stores information about annotation groups. :param annotationArray: stores annotation names of elements around. :param firstNodeIdentifier, firstElementIdentifier: first node and element identifier to use. :param useCubicHermiteThroughWall: use linear when false :param useCrossDerivatives: use cross derivatives when true :return nodeIdentifier, elementIdentifier, annotationGroups """ nodeIdentifier = firstNodeIdentifier elementIdentifier = firstElementIdentifier zero = [0.0, 0.0, 0.0] fm = region.getFieldmodule() fm.beginChange() cache = fm.createFieldcache() # Coordinates field coordinates = findOrCreateFieldCoordinates(fm) 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) if useCrossDerivatives: nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) if useCubicHermiteThroughWall: 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) mesh = fm.findMeshByDimension(3) if useCubicHermiteThroughWall: eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives) else: eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives) eft = eftfactory.createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate.defineField(coordinates, -1, eft) # Flat coordinates field bicubichermitelinear = eftfactory_bicubichermitelinear( mesh, useCrossDerivatives) eftTexture1 = bicubichermitelinear.createEftBasic() eftTexture2 = bicubichermitelinear.createEftOpenTube() flatCoordinates = findOrCreateFieldCoordinates(fm, name="flat coordinates") flatNodetemplate1 = nodes.createNodetemplate() flatNodetemplate1.defineField(flatCoordinates) flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_VALUE, 1) flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D_DS1, 1) flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCrossDerivatives: flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) flatNodetemplate2 = nodes.createNodetemplate() flatNodetemplate2.defineField(flatCoordinates) flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_VALUE, 2) flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D_DS1, 2) flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D_DS2, 2) if useCrossDerivatives: flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 2) flatElementtemplate1 = mesh.createElementtemplate() flatElementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) flatElementtemplate1.defineField(flatCoordinates, -1, eftTexture1) flatElementtemplate2 = mesh.createElementtemplate() flatElementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE) flatElementtemplate2.defineField(flatCoordinates, -1, eftTexture2) # Texture coordinates field textureCoordinates = findOrCreateFieldTextureCoordinates(fm) textureNodetemplate1 = nodes.createNodetemplate() textureNodetemplate1.defineField(textureCoordinates) textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_VALUE, 1) textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D_DS1, 1) textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCrossDerivatives: textureNodetemplate1.setValueNumberOfVersions( textureCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) textureNodetemplate2 = nodes.createNodetemplate() textureNodetemplate2.defineField(textureCoordinates) textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_VALUE, 2) textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D_DS1, 2) textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D_DS2, 2) if useCrossDerivatives: textureNodetemplate2.setValueNumberOfVersions( textureCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 2) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate1.defineField(textureCoordinates, -1, eftTexture1) elementtemplate2 = mesh.createElementtemplate() elementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate2.defineField(textureCoordinates, -1, eftTexture2) # Create nodes # Coordinates field for n in range(len(x)): node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x[n]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1[n]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2[n]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, d3[n]) if useCrossDerivatives: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) # print('NodeIdentifier = ', nodeIdentifier, xList[n]) nodeIdentifier = nodeIdentifier + 1 # Flat and texture coordinates fields nodeIdentifier = firstNodeIdentifier for n2 in range(elementsCountAlong + 1): for n3 in range(elementsCountThroughWall + 1): for n1 in range(elementsCountAround): i = n2 * (elementsCountAround + 1) * (elementsCountThroughWall + 1) + (elementsCountAround + 1) * n3 + n1 node = nodes.findNodeByIdentifier(nodeIdentifier) node.merge(flatNodetemplate2 if n1 == 0 else flatNodetemplate1) node.merge(textureNodetemplate2 if n1 == 0 else textureNodetemplate1) cache.setNode(node) # print('NodeIdentifier', nodeIdentifier, 'version 1, xList Index =', i+1) flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xFlat[i]) flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1Flat[i]) flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2Flat[i]) textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xTexture[i]) textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1Texture[i]) textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2Texture[i]) if useCrossDerivatives: flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) textureCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) if n1 == 0: # print('NodeIdentifier', nodeIdentifier, 'version 2, xList Index =', i+elementsCountAround+1) flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_VALUE, 2, xFlat[i + elementsCountAround]) flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS1, 2, d1Flat[i + elementsCountAround]) flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS2, 2, d2Flat[i + elementsCountAround]) textureCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_VALUE, 2, xTexture[i + elementsCountAround]) textureCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS1, 2, d1Texture[i + elementsCountAround]) textureCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS2, 2, d2Texture[i + elementsCountAround]) if useCrossDerivatives: flatCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero) textureCoordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero) nodeIdentifier = nodeIdentifier + 1 # create elements now = elementsCountAround * (elementsCountThroughWall + 1) for e2 in range(elementsCountAlong): for e3 in range(elementsCountThroughWall): for e1 in range(elementsCountAround): bni11 = e2 * now + e3 * elementsCountAround + e1 + 1 bni12 = e2 * now + e3 * elementsCountAround + ( e1 + 1) % elementsCountAround + 1 bni21 = e2 * now + (e3 + 1) * elementsCountAround + e1 + 1 bni22 = e2 * now + (e3 + 1) * elementsCountAround + ( e1 + 1) % elementsCountAround + 1 nodeIdentifiers = [ bni11, bni12, bni11 + now, bni12 + now, bni21, bni22, bni21 + now, bni22 + now ] onOpening = e1 > elementsCountAround - 2 element = mesh.createElement(elementIdentifier, elementtemplate) element.setNodesByIdentifier(eft, nodeIdentifiers) element.merge(flatElementtemplate2 if onOpening else flatElementtemplate1) element.merge( elementtemplate2 if onOpening else elementtemplate1) element.setNodesByIdentifier( eftTexture2 if onOpening else eftTexture1, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 if annotationGroups: for annotationGroup in annotationGroups: if annotationArray[e1] == annotationGroup._name: meshGroup = annotationGroup.getMeshGroup(mesh) meshGroup.addElement(element) fm.endChange() return nodeIdentifier, elementIdentifier, annotationGroups
def generateMesh(region, options): """ :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: None """ elementsCountUp = options['Number of elements up'] elementsCountUpSeptum = options['Number of elements up septum'] elementsCountAround = options['Number of elements around'] #print('elementsCountAround', elementsCountAround) elementsCountAlongSeptum = options['Number of extra elements along septum'] atriaAxisRadians = math.radians(options['Major axis rotation degrees']) totalArcUpRadians = math.radians(options['Total arc up degrees']) septumArcUpRadians = totalArcUpRadians*options['Septum arc up ratio'] lengthRatio = options['Length ratio'] baseSeptumThickness = options['Base septum thickness'] freeWallThickness = options['Free wall thickness'] majorAxisRadians = math.radians(options['Major axis rotation degrees']) baseToEquatorRatio = 1.0/math.sin(totalArcUpRadians) innerMajorMag = 0.5*options['Base inner major axis length'] innerMinorMag = 0.5*options['Base inner minor axis length'] vcInnerDiameter = options['Vena cava inner diameter'] vcWallThickness = options['Vena cava wall thickness'] pvInnerDiameter = options['Pulmonary vein inner diameter'] pvWallThickness = options['Pulmonary vein wall thickness'] useCrossDerivatives = options['Use cross derivatives'] fm = region.getFieldmodule() fm.beginChange() coordinates = getOrCreateCoordinateField(fm) cache = fm.createFieldcache() nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) nodetemplateApex = nodes.createNodetemplate() nodetemplateApex.defineField(coordinates) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) if useCrossDerivatives: nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) 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) else: nodetemplate = nodetemplateApex ############## # Create nodes ############## nodeIdentifier = 1 outerMajorMag = innerMajorMag + freeWallThickness outerMinorMag = innerMinorMag + freeWallThickness laInnerMajorX = innerMajorMag*math.sin(majorAxisRadians) laInnerMajorY = innerMajorMag*math.cos(majorAxisRadians) laInnerMinorX = -innerMinorMag*math.cos(majorAxisRadians) laInnerMinorY = innerMinorMag*math.sin(majorAxisRadians) laOuterMajorX = outerMajorMag*math.sin(majorAxisRadians) laOuterMajorY = outerMajorMag*math.cos(majorAxisRadians) laOuterMinorX = -outerMinorMag*math.cos(majorAxisRadians) laOuterMinorY = outerMinorMag*math.sin(majorAxisRadians) # following is radians around LA laSeptumRadians = math.atan2(laInnerMinorY, laInnerMajorY) laSeptumX = 0.0 laSeptumY = -0.5*baseSeptumThickness laCentreX = laSeptumX - laInnerMajorX*math.cos(laSeptumRadians) - laInnerMinorX*math.sin(laSeptumRadians) laCentreY = laSeptumY - laInnerMajorY*math.cos(laSeptumRadians) - laInnerMinorY*math.sin(laSeptumRadians) raSeptumX = 0.0 raSeptumY = 0.5*baseSeptumThickness raSeptumRadians = math.pi*2.0 - laSeptumRadians raCentreX = laCentreX raCentreY = -laCentreY raInnerMajorX = innerMajorMag*math.sin(majorAxisRadians) raInnerMajorY = -innerMajorMag*math.cos(majorAxisRadians) raInnerMinorX = innerMinorMag*math.cos(majorAxisRadians) raInnerMinorY = innerMinorMag*math.sin(majorAxisRadians) raOuterMajorX = outerMajorMag*math.sin(majorAxisRadians) raOuterMajorY = -outerMajorMag*math.cos(majorAxisRadians) raOuterMinorX = outerMinorMag*math.cos(majorAxisRadians) raOuterMinorY = outerMinorMag*math.sin(majorAxisRadians) laRadians = [0.0]*elementsCountAround laRadians[0] = laSeptumRadians deltaRadians = 2.0*math.pi/elementsCountAround laDeltaRadians = [ deltaRadians ]*elementsCountAround for i in range(1, elementsCountAround): laRadians[i] = laRadians[i - 1] + deltaRadians raRadians = [0.0]*elementsCountAround raRadians[0] = raSeptumRadians raDeltaRadians = [ deltaRadians ]*elementsCountAround for i in range(1, elementsCountAround): raRadians[i] = raRadians[i - 1] + deltaRadians upRadians = [ [ 0.0 ]*(elementsCountUp + 1), [ 0.0 ]*(elementsCountUp + 1) ] deltaUpRadians = [ [ 0.0 ]*(elementsCountUp + 1), [ 0.0 ]*(elementsCountUp + 1) ] baseRadiansUp = math.pi - totalArcUpRadians deltaRadians1 = septumArcUpRadians/elementsCountUpSeptum deltaRadiansN = (totalArcUpRadians - septumArcUpRadians - 0.5*deltaRadians1) / (elementsCountUp - elementsCountUpSeptum - 0.5) for i in range(elementsCountUpSeptum + 1): upRadians[0][i] = baseRadiansUp + i*deltaRadians1 deltaUpRadians[0][i] = deltaRadians1 for i in range(elementsCountUp - elementsCountUpSeptum): upRadians[0][elementsCountUp - i] = math.pi - i*deltaRadiansN deltaUpRadians[0][elementsCountUp - i] = deltaRadiansN # GRC temp: should make separate range to get inlet incline at bottom: for i in range(elementsCountUp + 1): upRadians[1][i] = upRadians[0][i] deltaUpRadians[1][i] = deltaUpRadians[0][i] #print('upRadians',upRadians[0]) #print('deltaUpRadians',deltaUpRadians[0]) innerScaleZ = (lengthRatio - freeWallThickness)/(1.0 - math.cos(totalArcUpRadians)) outerScaleZ = lengthRatio/(1.0 - math.cos(totalArcUpRadians)) laNodeId = [ [], [] ] raNodeId = [ [], [] ] laApexNodeId = [ -1 ]*2 raApexNodeId = [ -1 ]*2 for n3 in range(2): for n2 in range(elementsCountUp): # GRC support separate inner and outer radians up radiansUp = upRadians[n3][n2] scalingUp = baseToEquatorRatio*math.sin(radiansUp) innerZ = -innerScaleZ*math.cos(radiansUp) outerZ = -outerScaleZ*math.cos(radiansUp) laLayerNodeId = [-1]*elementsCountAround laNodeId[n3].append(laLayerNodeId) raLayerNodeId = [-1]*elementsCountAround raNodeId[n3].append(raLayerNodeId) # regular nodes up atria for i in range(2): if i == 0: # left centreX, centreY = laCentreX, laCentreY aRadians = laRadians aDeltaRadians = laDeltaRadians layerNodeId = laLayerNodeId innerMajorX, innerMajorY = laInnerMajorX, laInnerMajorY innerMinorX, innerMinorY = laInnerMinorX, laInnerMinorY outerMajorX, outerMajorY = laOuterMajorX, laOuterMajorY outerMinorX, outerMinorY = laOuterMinorX, laOuterMinorY else: # right centreX, centreY = raCentreX, raCentreY aRadians = raRadians aDeltaRadians = raDeltaRadians layerNodeId = raLayerNodeId innerMajorX, innerMajorY = raInnerMajorX, raInnerMajorY innerMinorX, innerMinorY = raInnerMinorX, raInnerMinorY outerMajorX, outerMajorY = raOuterMajorX, raOuterMajorY outerMinorX, outerMinorY = raOuterMinorX, raOuterMinorY for n1 in range(elementsCountAround): radiansAround = aRadians[n1] cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) inner = [ centreX + scalingUp*(cosRadiansAround*innerMajorX + sinRadiansAround*innerMinorX), centreY + scalingUp*(cosRadiansAround*innerMajorY + sinRadiansAround*innerMinorY), innerZ ] outer = [ centreX + scalingUp*(cosRadiansAround*outerMajorX + sinRadiansAround*outerMinorX), centreY + scalingUp*(cosRadiansAround*outerMajorY + sinRadiansAround*outerMinorY), outerZ ] if (n3 == 1) and (n2 <= elementsCountUpSeptum) and (n1 == 0): continue # right septum node created in next loop node = nodes.createNode(nodeIdentifier, nodetemplate) layerNodeId[n1] = nodeIdentifier cache.setNode(node) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, inner if (n3 == 0) else outer) if n3 == 0: dx_ds1 = [ scalingUp*aDeltaRadians[n1]*(-sinRadiansAround*innerMajorX + cosRadiansAround*innerMinorX), scalingUp*aDeltaRadians[n1]*(-sinRadiansAround*innerMajorY + cosRadiansAround*innerMinorY), 0.0 ] dx_ds2 = [ deltaUpRadians[n3][n2]*math.cos(radiansUp)*(cosRadiansAround*innerMajorX + sinRadiansAround*innerMinorX), deltaUpRadians[n3][n2]*math.cos(radiansUp)*(cosRadiansAround*innerMajorY + sinRadiansAround*innerMinorY), deltaUpRadians[n3][n2]*math.sin(radiansUp)*innerScaleZ ] else: dx_ds1 = [ scalingUp*aDeltaRadians[n1]*(-sinRadiansAround*outerMajorX + cosRadiansAround*outerMinorX), scalingUp*aDeltaRadians[n1]*(-sinRadiansAround*outerMajorY + cosRadiansAround*outerMinorY), 0.0 ] dx_ds2 = [ deltaUpRadians[n3][n2]*math.cos(radiansUp)*(cosRadiansAround*outerMajorX + sinRadiansAround*outerMinorX), deltaUpRadians[n3][n2]*math.cos(radiansUp)*(cosRadiansAround*outerMajorY + sinRadiansAround*outerMinorY), deltaUpRadians[n3][n2]*math.sin(radiansUp)*outerScaleZ ] dx_ds3 = [ outer[0] - inner[0], outer[1] - inner[1], outer[2] - inner[2] ] coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) nodeIdentifier += 1 # apexes for i in range(2): if i == 0: # left centreX, centreY = laCentreX, laCentreY apexNodeId = laApexNodeId innerMajorX, innerMajorY = laInnerMajorX, laInnerMajorY innerMinorX, innerMinorY = laInnerMinorX, laInnerMinorY outerMajorX, outerMajorY = laOuterMajorX, laOuterMajorY outerMinorX, outerMinorY = laOuterMinorX, laOuterMinorY else: # right centreX, centreY = raCentreX, raCentreY aRadians = raRadians apexNodeId = raApexNodeId innerMajorX, innerMajorY = raInnerMajorX, raInnerMajorY innerMinorX, innerMinorY = raInnerMinorX, raInnerMinorY outerMajorX, outerMajorY = raOuterMajorX, raOuterMajorY outerMinorX, outerMinorY = raOuterMinorX, raOuterMinorY x = [ centreX, centreY, innerScaleZ if (n3 == 0) else outerScaleZ ] if n3 == 0: dx_ds1 = [ baseToEquatorRatio*deltaUpRadians[n3][-1]*innerMajorX, baseToEquatorRatio*deltaUpRadians[n3][-1]*innerMajorY, 0.0 ] dx_ds2 = [ baseToEquatorRatio*deltaUpRadians[n3][-1]*innerMinorX, baseToEquatorRatio*deltaUpRadians[n3][-1]*innerMinorY, 0.0 ] else: dx_ds1 = [ baseToEquatorRatio*deltaUpRadians[n3][-1]*outerMajorX, baseToEquatorRatio*deltaUpRadians[n3][-1]*outerMajorY, 0.0 ] dx_ds2 = [ baseToEquatorRatio*deltaUpRadians[n3][-1]*outerMinorX, baseToEquatorRatio*deltaUpRadians[n3][-1]*outerMinorY, 0.0 ] dx_ds3 = [ 0.0, 0.0, freeWallThickness ] node = nodes.createNode(nodeIdentifier, nodetemplate) apexNodeId[n3] = nodeIdentifier cache.setNode(node) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) nodeIdentifier += 1 # transfer inner septum nodes to outer on opposite side, set derivative 3 to be node difference for n2 in range(elementsCountUpSeptum + 1): laNodeId[1][n2][0] = raNodeId[0][n2][0] raNodeId[1][n2][0] = laNodeId[0][n2][0] node2 = nodes.findNodeByIdentifier(laNodeId[1][n2][0]) cache.setNode(node2) result, x_o = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) node1 = nodes.findNodeByIdentifier(laNodeId[0][n2][0]) cache.setNode(node1) result, x_i = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) dx_ds3 = [ (x_o[i] - x_i[i]) for i in range(3) ] result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) dx_ds3 = [ -v for v in dx_ds3 ] cache.setNode(node2) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) # create extra node(s) at top of septum n2 = elementsCountUpSeptum + 1 node1 = nodes.findNodeByIdentifier(laNodeId[1][n2][0]) cache.setNode(node1) result, v1 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) result, d1 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, 3) d1 = [ -d for d in d1 ] node2 = nodes.findNodeByIdentifier(raNodeId[1][n2][0]) cache.setNode(node2) result, v2 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) result, d2 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, 3) xi = 0.5 vc = interpolateCubicHermite(v1, d1, v2, d2, xi ) dc = interpolateCubicHermiteDerivative(v1, d1, v2, d2, xi ) x = [ vc[0], vc[1], vc[2] ] # get magnitude of dx_ds1 from arc around apex to next node node = nodes.findNodeByIdentifier(apexNodeId[0]) cache.setNode(node) result, ac = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) node = nodes.findNodeByIdentifier(laNodeId[1][n2 - 1][1]) cache.setNode(node) result, vb = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) a = [ (vc[i] - ac[i]) for i in range(3) ] b = [ (vb[i] - ac[i]) for i in range(3) ] mag_a = math.sqrt(a[0]*a[0] + a[1]*a[1] + a[1]*a[1]) mag_b = math.sqrt(b[0]*b[0] + b[1]*b[1] + b[1]*b[1]) arcRadians = math.acos((a[0]*b[0] + a[1]*b[1] + a[1]*b[1]) / (mag_a*mag_b)) mag = 0.5*(mag_a + mag_b)*arcRadians dx_ds1 = [ mag, 0.0, 0.0 ] dx_ds2 = [ 0.5*d for d in dc ] dx_ds3 = [ 0.0, 0.0, vc[2] + innerScaleZ*math.cos(math.pi - totalArcUpRadians + septumArcUpRadians) ] node = nodes.createNode(nodeIdentifier, nodetemplate) septumNodeId = nodeIdentifier cache.setNode(node) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) #print('septum', node.isValid(), result, ' nodes', laNodeId[1][n2][0], raNodeId[1][n2][0]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) nodeIdentifier += 1 if False: # show centre/axes of atria node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, [ laCentreX, laCentreY, innerScaleZ*math.cos(totalArcUpRadians) ]) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, [ laInnerMajorX, laInnerMajorY, 0.0 ]) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, [ laInnerMinorX, laInnerMinorY, 0.0 ]) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, [ 0.0, 0.0, lengthRatio - freeWallThickness ]) nodeIdentifier += 1 # show axes of right atrium node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, [ raCentreX, raCentreY, innerScaleZ*math.cos(totalArcUpRadians) ]) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, [ raInnerMajorX, raInnerMajorY, 0.0 ]) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, [ raInnerMinorX, raInnerMinorY, 0.0 ]) result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, [ 0.0, 0.0, lengthRatio - freeWallThickness ]) nodeIdentifier += 1 ################# # Create elements ################# elementIdentifier = 1 mesh = fm.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) tricubicHermiteBasis = fm.createElementbasis(3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) eft = tricubichermite.createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate.defineField(coordinates, -1, eft) eftSeptumSplitRight = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eftSeptumSplitRight, [1], []) remapEftNodeValueLabel(eftSeptumSplitRight, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eftSeptumSplitRight, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, []) ]) #scaleEftNodeValueLabels(eftSeptumSplitRight, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) elementtemplateSeptumSplitRight = mesh.createElementtemplate() elementtemplateSeptumSplitRight.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplateSeptumSplitRight.defineField(coordinates, -1, eftSeptumSplitRight) eftSeptumSplitCentre = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eftSeptumSplitCentre, [1], []) remapEftNodeValueLabel(eftSeptumSplitCentre, [ 5, 7 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [1]) ]) remapEftNodeValueLabel(eftSeptumSplitCentre, [ 6, 8 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [1]) ]) remapEftNodeValueLabel(eftSeptumSplitCentre, [ 6, 8 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS3, [1]) ]) elementtemplateSeptumSplitCentre = mesh.createElementtemplate() elementtemplateSeptumSplitCentre.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplateSeptumSplitCentre.defineField(coordinates, -1, eftSeptumSplitCentre) # regular rows within septum for e2 in range(elementsCountUpSeptum): for i in range(2): if i == 0: # left nodeId = laNodeId otherNodeId = raNodeId else: # right nodeId = raNodeId otherNodeId = laNodeId 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 e1 == 0: eft1 = eftSeptumSplitRight elementtemplate1 = elementtemplateSeptumSplitRight nids[4] = otherNodeId[1][e2][-1] nids[6] = otherNodeId[1][e2 + 1][-1] elif e1 == (elementsCountAround - 1): eft1 = eftSeptumSplitCentre elementtemplate1 = elementtemplateSeptumSplitCentre else: eft1 = eft elementtemplate1 = elementtemplate element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) if eft1.getNumberOfLocalScaleFactors() == 1: result3 = element.setScaleFactors(eft1, [ -1.0 ]) else: result3 = 1 #print('create element', 'la' if i == 0 else 'ra', element.isValid(), elementIdentifier, result2, result3, nids) elementIdentifier += 1 # septum transition interior collapsed elements n2 = elementsCountUpSeptum nids = [ [ laNodeId[0][n2][ 0], laNodeId[0][n2][1], raNodeId[1][n2][-1], laNodeId[1][n2][1], septumNodeId ], [ laNodeId[0][n2][-1], laNodeId[0][n2][0], laNodeId[1][n2][-1], laNodeId[1][n2][0], septumNodeId ], [ raNodeId[0][n2][ 0], raNodeId[0][n2][1], laNodeId[1][n2][-1], raNodeId[1][n2][1], septumNodeId ], [ raNodeId[0][n2][-1], raNodeId[0][n2][0], raNodeId[1][n2][-1], raNodeId[1][n2][0], septumNodeId ] ] for e in range(len(nids)): eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) if e in [ 0, 2 ]: remapEftNodeValueLabel(eft1, [ 1, 2, 3, 4, 6, 8 ], Node.VALUE_LABEL_D_DS2, [ ] ) remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, []) ]) if e == 0: #remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [1]) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [1]) ]) #remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, []) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [1]) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, []) ]) else: remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, []) ]) #remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, [1]) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, [1]), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [1]) ]) ln_map = [ 1, 2, 1, 2, 3, 4, 5, 4 ] remapEftLocalNodes(eft1, 5, ln_map) else: remapEftNodeValueLabel(eft1, [ 1, 2, 3, 4, 5, 7 ], Node.VALUE_LABEL_D_DS2, [ ] ) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ (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]), (Node.VALUE_LABEL_D_DS3, [1]) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS3, [1]) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, []) ]) if e == 1: #remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [1]) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS2, [1]), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, []) ]) else: remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [1]) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, [1]), (Node.VALUE_LABEL_D_DS3, []) ]) ln_map = [ 1, 2, 1, 2, 3, 4, 3, 5 ] remapEftLocalNodes(eft1, 5, ln_map) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids[e]) if eft1.getNumberOfLocalScaleFactors() == 1: result3 = element.setScaleFactors(eft1, [ -1.0 ]) else: result3 = 1 #print('create element st', elementIdentifier, result, result2, result3, nids[e]) elementIdentifier += 1 # semi-regular rows above septum but below apex, including second septum transition elements for e2 in range(elementsCountUpSeptum, elementsCountUp - 1): for i in range(2): if i == 0: # left nodeId = laNodeId otherNodeId = raNodeId else: # right nodeId = raNodeId otherNodeId = laNodeId 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 (e2 == elementsCountUpSeptum) and ((e1 == 0) or (e1 == elementsCountAround - 1)): eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) if e1 == 0: nids[4] = septumNodeId remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [1 if (i == 0) else 0]), (Node.VALUE_LABEL_D_DS2, [1 if (i == 0) else 0]) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS2, [1 if (i == 0) else 0]) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, [1 if (i == 0) else 0]), (Node.VALUE_LABEL_D_DS3, [0]) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [1]) ]) else: nids[5] = septumNodeId remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [1 if (i == 0) else 0]), (Node.VALUE_LABEL_D_DS2, [0 if (i == 0) else 1]) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS2, [1 if (i == 0) else 0]) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ (Node.VALUE_LABEL_D_DS2, [1 if (i == 0) else 0]), (Node.VALUE_LABEL_D_DS3, [0]) ]) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) else: eft1 = eft elementtemplate1 = elementtemplate element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) if eft1.getNumberOfLocalScaleFactors() == 1: result3 = element.setScaleFactors(eft1, [ -1.0 ]) else: result3 = 1 #print('create element', 'la' if i == 0 else 'ra', element.isValid(), elementIdentifier, result2, result3, nids) if e2 == elementsCountUp - 2: if i == 0: if e1 == 0: rapvElementId = elementIdentifier elif e1 == (elementsCountAround - 1): rppvElementId = elementIdentifier elif e1 == (elementsCountAround//2 - 1): lapvElementId = elementIdentifier elif e1 == ((elementsCountAround + 1)//2): lppvElementId = elementIdentifier else: # i == 1: if e1 == 1: ivcElementId = elementIdentifier elif e1 == (elementsCountAround - 2): svcElementId = elementIdentifier elementIdentifier += 1 for i in range(2): nodeId = laNodeId if (i == 0) else raNodeId apexNodeId = laApexNodeId if (i == 0) else raApexNodeId aRadians = laRadians if (i == 0) else raRadians deltaRadians = laDeltaRadians if (i == 0) else raDeltaRadians # create top apex elements # scale factor identifiers follow convention of offsetting by 100 for each 'version' elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1)%elementsCountAround eft1 = tricubichermite.createEftShellApexTop(va*100, vb*100) elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) n2 = elementsCountUp - 1 nodeIdentifiers = [ nodeId[0][n2][va], nodeId[0][n2][vb], apexNodeId[0], nodeId[1][n2][va], nodeId[1][n2][vb], apexNodeId[1] ] element.setNodesByIdentifier(eft1, nodeIdentifiers) # set general linear map coefficients scalefactors = [ -1.0, -math.cos(aRadians[va]), -math.sin(aRadians[va]), deltaRadians[va], -math.cos(aRadians[vb]), -math.sin(aRadians[vb]), deltaRadians[vb], -math.cos(aRadians[va]), -math.sin(aRadians[va]), deltaRadians[va], -math.cos(aRadians[vb]), -math.sin(aRadians[vb]), deltaRadians[vb] ] result = element.setScaleFactors(eft1, scalefactors) elementIdentifier = elementIdentifier + 1 # add right atria inlets (venae cavae) for elementId in [ ivcElementId, svcElementId ]: element = mesh.findElementByIdentifier(elementId) vcLength = vcInnerDiameter*0.5 tricubichermite.replaceElementWithInlet4(element, elementIdentifier, nodetemplate, nodeIdentifier, vcLength, vcInnerDiameter, vcWallThickness) elementIdentifier += 4 nodeIdentifier += 8 mesh.destroyElement(element) # add left atria inlets (pulmonary veins) for elementId in [ lapvElementId, lppvElementId, rapvElementId, rppvElementId ]: element = mesh.findElementByIdentifier(elementId) pvLength = pvInnerDiameter*0.5 tricubichermite.replaceElementWithInlet4(element, elementIdentifier, nodetemplate, nodeIdentifier, pvLength, pvInnerDiameter, pvWallThickness) elementIdentifier += 4 nodeIdentifier += 8 mesh.destroyElement(element) fm.endChange()
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 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: [] empty list of AnnotationGroup """ elementsCountAround = options['Number of elements around'] elementsCountUp = options['Number of elements up'] elementsCountRadial = options['Number of elements radial'] useCrossDerivatives = options['Use cross derivatives'] radius = 0.5 * options['Diameter'] fm = region.getFieldmodule() fm.beginChange() coordinates = findOrCreateFieldCoordinates(fm) nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) nodetemplateApex = nodes.createNodetemplate() nodetemplateApex.defineField(coordinates) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) if useCrossDerivatives: nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) 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) else: nodetemplate = nodetemplateApex cache = fm.createFieldcache() ################# # Create nodes ################# nodeIdentifier = 1 radiansPerElementAround = 2.0 * math.pi / elementsCountAround radiansPerElementUp = math.pi / elementsCountUp x = [0.0, 0.0, 0.0] dx_ds1 = [0.0, 0.0, 0.0] dx_ds2 = [0.0, 0.0, 0.0] dx_ds3 = [0.0, 0.0, 0.0] zero = [0.0, 0.0, 0.0] cubicArcLengthList = [0.0] * (elementsCountUp + 1) # Pre-calculate cubicArcLength along elementsCountUp for n2 in range(1, elementsCountUp + 1): radiansUp = n2 * radiansPerElementUp cosRadiansUp = math.cos(radiansUp) sinRadiansUp = math.sin(radiansUp) # Calculate cubic hermite arclength linking point on axis to surface on sphere v1 = [0.0, 0.0, -radius + n2 * 2.0 * radius / elementsCountUp] d1 = [0.0, 1.0, 0.0] v2 = [ radius * math.cos(math.pi / 2.0) * sinRadiansUp, radius * math.sin(math.pi / 2.0) * sinRadiansUp, -radius * cosRadiansUp ] d2 = [ math.cos(math.pi / 2.0) * sinRadiansUp, math.sin(math.pi / 2.0) * sinRadiansUp, -cosRadiansUp ] cubicArcLengthList[n2] = interp.computeCubicHermiteArcLength( v1, d1, v2, d2, True) # Create node for bottom pole node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, [0.0, 0.0, -radius]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, [radius * radiansPerElementUp, 0.0, 0.0]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, [0.0, radius * radiansPerElementUp, 0.0]) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS3, 1, [0.0, 0.0, -radius * 2.0 / elementsCountUp]) if useCrossDerivatives: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 # Create nodes along axis between top and bottom poles for n2 in range(1, elementsCountUp): node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_VALUE, 1, [0.0, 0.0, -radius + n2 * 2.0 * radius / elementsCountUp]) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS1, 1, [cubicArcLengthList[n2] / elementsCountRadial, 0.0, 0.0]) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS2, 1, [0.0, 0.0, radius * 2.0 / elementsCountUp]) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS3, 1, [0.0, cubicArcLengthList[n2] / elementsCountRadial, 0.0]) if useCrossDerivatives: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 # Create nodes for top pole node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, [0.0, 0.0, radius]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, [radius * radiansPerElementUp, 0.0, 0.0]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, [0.0, radius * radiansPerElementUp, 0.0]) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D_DS3, 1, [0.0, 0.0, radius * 2.0 / elementsCountUp]) if useCrossDerivatives: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 # Create other nodes for n3 in range(1, elementsCountRadial + 1): xi = 1 / elementsCountRadial * n3 radiansUpArcOriginList = [0.0] * (elementsCountUp) # Pre-calculate RC for points on vertical arc running between top and bottom poles pt = [0.0, radius * xi, 0.0] arcOrigin = (radius * radius - pt[2] * pt[2] - pt[1] * pt[1]) / (-2.0 * pt[1]) RC = math.sqrt(arcOrigin * arcOrigin + radius * radius) radiansUpArcOriginList[0] = math.acos(-radius / RC) # Identify nodes on the vertical arc using radiansAround = pi/2 for n2 in range(1, elementsCountUp): radiansUp = n2 * radiansPerElementUp cosRadiansUp = math.cos(radiansUp) sinRadiansUp = math.sin(radiansUp) # Calculate node coordinates on arc using cubic hermite interpolation cubicArcLength = cubicArcLengthList[n2] v1 = [0.0, 0.0, -radius + n2 * 2.0 * radius / elementsCountUp] d1 = [math.cos(math.pi / 2.0), math.sin(math.pi / 2.0), 0.0] d1 = vector.normalise(d1) d1 = [d * cubicArcLength for d in d1] v2 = [ radius * math.cos(math.pi / 2.0) * sinRadiansUp, radius * math.sin(math.pi / 2.0) * sinRadiansUp, -radius * cosRadiansUp ] d2 = [ math.cos(math.pi / 2.0) * sinRadiansUp, math.sin(math.pi / 2.0) * sinRadiansUp, -cosRadiansUp ] d2 = vector.normalise(d2) d2 = [d * cubicArcLength for d in d2] x = interp.interpolateCubicHermite(v1, d1, v2, d2, xi) # Calculate radiansUp for each point wrt arcOrigin radiansUpArcOriginList[n2] = math.acos(x[2] / RC) for n2 in range(1, elementsCountUp): radiansUp = n2 * radiansPerElementUp cosRadiansUp = math.cos(radiansUp) sinRadiansUp = math.sin(radiansUp) for n1 in range(elementsCountAround): radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) cubicArcLength = cubicArcLengthList[n2] # Calculate node coordinates on arc using cubic hermite interpolation v1 = [ 0.0, 0.0, -radius + n2 * 2.0 * radius / elementsCountUp ] d1 = [cosRadiansAround, sinRadiansAround, 0.0] d1 = vector.normalise(d1) d1 = [d * cubicArcLength for d in d1] v2 = [ radius * cosRadiansAround * sinRadiansUp, radius * sinRadiansAround * sinRadiansUp, -radius * cosRadiansUp ] d2 = [ cosRadiansAround * sinRadiansUp, sinRadiansAround * sinRadiansUp, -cosRadiansUp ] d2 = vector.normalise(d2) d2 = [d * cubicArcLength for d in d2] x = interp.interpolateCubicHermite(v1, d1, v2, d2, xi) # For dx_ds1 - Calculate radius wrt origin where interpolated points lie on orthoRadius = vector.magnitude(x) orthoRadiansUp = math.pi - math.acos(x[2] / orthoRadius) sinOrthoRadiansUp = math.sin(orthoRadiansUp) cosOrthoRadiansUp = math.cos(orthoRadiansUp) # For dx_ds2 - Assign radiansUp from radiansUpArcOriginList and calculate diff between radiansUp as we move up radiansUpArcOrigin = radiansUpArcOriginList[n2] sinRadiansUpArcOrigin = math.sin(radiansUpArcOrigin) cosRadiansUpArcOrigin = math.cos(radiansUpArcOrigin) radiansPerElementUpArcOrigin = radiansUpArcOriginList[ n2] - radiansUpArcOriginList[n2 - 1] dx_ds1 = [ orthoRadius * -sinRadiansAround * sinOrthoRadiansUp * radiansPerElementAround, orthoRadius * cosRadiansAround * sinOrthoRadiansUp * radiansPerElementAround, 0.0 ] dx_ds2 = [ RC * cosRadiansAround * cosRadiansUpArcOrigin * radiansPerElementUpArcOrigin, RC * sinRadiansAround * cosRadiansUpArcOrigin * radiansPerElementUpArcOrigin, -RC * sinRadiansUpArcOrigin * radiansPerElementUpArcOrigin ] dx_ds3 = interp.interpolateCubicHermiteDerivative( v1, d1, v2, d2, xi) dx_ds3 = vector.normalise(dx_ds3) dx_ds3 = [ d * cubicArcLength / elementsCountRadial for d in dx_ds3 ] node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) if useCrossDerivatives: coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 ################# # Create elements ################# mesh = fm.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftBasic() tricubicHermiteBasis = fm.createElementbasis( 3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) # Regular elements elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate.defineField(coordinates, -1, eft) # Bottom tetrahedon elements elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) # Axial elements elementtemplate2 = mesh.createElementtemplate() elementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE) # Top tetrahedron elements elementtemplate3 = mesh.createElementtemplate() elementtemplate3.setElementShapeType(Element.SHAPE_TYPE_CUBE) # Bottom pyramid elements elementtemplate4 = mesh.createElementtemplate() elementtemplate4.setElementShapeType(Element.SHAPE_TYPE_CUBE) # Top pyramid elements elementtemplate5 = mesh.createElementtemplate() elementtemplate5.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementIdentifier = 1 no2 = elementsCountAround no3 = elementsCountAround * (elementsCountUp - 1) rni = (1 + elementsCountUp) - no3 - no2 + 1 # regular node identifier radiansPerElementAround = 2.0 * math.pi / elementsCountAround for e3 in range(elementsCountRadial): # Create elements on bottom pole radiansIncline = math.pi * 0.5 * e3 / elementsCountRadial radiansInclineNext = math.pi * 0.5 * (e3 + 1) / elementsCountRadial if e3 == 0: # Create tetrahedron elements on the bottom pole bni1 = elementsCountUp + 2 for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eft1 = tricubichermite.createEftTetrahedronBottom( va * 100, vb * 100, 10000) elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers = [1, 2, bni1 + va, bni1 + vb] result1 = element.setNodesByIdentifier( eft1, nodeIdentifiers) # set general linear map coefficients radiansAround = va * radiansPerElementAround radiansAroundNext = vb * radiansPerElementAround scalefactors = [ -1.0, math.cos(radiansAround), math.sin(radiansAround), radiansPerElementAround, math.cos(radiansAroundNext), math.sin(radiansAroundNext), radiansPerElementAround, math.cos(radiansAround), math.sin(radiansAround), radiansPerElementAround, math.cos(radiansAroundNext), math.sin(radiansAroundNext), radiansPerElementAround, math.cos(radiansIncline), math.sin(radiansIncline), math.cos(radiansInclineNext), math.sin(radiansInclineNext) ] result2 = element.setScaleFactors(eft1, scalefactors) # print('Tetrahedron Bottom element', elementIdentifier, result1, result2, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 else: # Create pyramid elements on the bottom pole bni4 = elementsCountUp + 1 + (e3 - 1) * no3 + 1 for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eft4 = tricubichermite.createEftPyramidBottom( va * 100, vb * 100, 100000 + e3 * 2) elementtemplate4.defineField(coordinates, -1, eft4) element = mesh.createElement(elementIdentifier, elementtemplate4) nodeIdentifiers = [ 1, bni4 + va, bni4 + vb, bni4 + no3 + va, bni4 + no3 + vb ] result1 = element.setNodesByIdentifier( eft4, nodeIdentifiers) # set general linear map coefficients radiansAround = va * radiansPerElementAround radiansAroundNext = vb * radiansPerElementAround scalefactors = [ -1.0, math.cos(radiansAround), math.sin(radiansAround), radiansPerElementAround, math.cos(radiansAroundNext), math.sin(radiansAroundNext), radiansPerElementAround, math.cos(radiansIncline), math.sin(radiansIncline), math.cos(radiansInclineNext), math.sin(radiansInclineNext) ] result2 = element.setScaleFactors(eft4, scalefactors) # print('pyramid bottom element', elementIdentifier, result1, result2, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 # create regular radial elements for e2 in range(1, elementsCountUp - 1): if e3 == 0: for e1 in range(elementsCountAround): # create central radial elements: 6 node wedges va = e1 vb = (e1 + 1) % elementsCountAround eft2 = tricubichermite.createEftWedgeRadial( va * 100, vb * 100) elementtemplate2.defineField(coordinates, -1, eft2) element = mesh.createElement(elementIdentifier, elementtemplate2) bni2 = elementsCountUp + 1 + (e2 - 1) * no2 + 1 nodeIdentifiers = [ e3 + e2 + 1, e3 + e2 + 2, bni2 + va, bni2 + vb, bni2 + va + elementsCountAround, bni2 + vb + elementsCountAround ] result1 = element.setNodesByIdentifier( eft2, nodeIdentifiers) # set general linear map coefficients radiansAround = va * radiansPerElementAround radiansAroundNext = vb * radiansPerElementAround scalefactors = [ -1.0, math.cos(radiansAround), math.sin(radiansAround), radiansPerElementAround, math.cos(radiansAroundNext), math.sin(radiansAroundNext), radiansPerElementAround, math.cos(radiansAround), math.sin(radiansAround), radiansPerElementAround, math.cos(radiansAroundNext), math.sin(radiansAroundNext), radiansPerElementAround ] result2 = element.setScaleFactors(eft2, scalefactors) # print('axis element', elementIdentifier, result1, result2, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 else: # Regular elements bni = rni + e3 * no3 + e2 * no2 for e1 in range(elementsCountAround): element = mesh.createElement(elementIdentifier, elementtemplate) na = e1 nb = (e1 + 1) % elementsCountAround nodeIdentifiers = [ bni + na, bni + nb, bni + no2 + na, bni + no2 + nb, bni + no3 + na, bni + no3 + nb, bni + no3 + no2 + na, bni + no3 + no2 + nb ] result = element.setNodesByIdentifier( eft, nodeIdentifiers) # print('regular element', elementIdentifier, result, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 # Create elements on top pole radiansIncline = math.pi * 0.5 * e3 / elementsCountRadial radiansInclineNext = math.pi * 0.5 * (e3 + 1) / elementsCountRadial if e3 == 0: # # Create tetrahedron elements on the top pole bni3 = elementsCountUp + 1 + (elementsCountUp - 2) * no2 + 1 for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eft3 = tricubichermite.createEftTetrahedronTop( va * 100, vb * 100, 100000) elementtemplate3.defineField(coordinates, -1, eft3) element = mesh.createElement(elementIdentifier, elementtemplate3) nodeIdentifiers = [ elementsCountUp, elementsCountUp + 1, bni3 + va, bni3 + vb ] result1 = element.setNodesByIdentifier( eft3, nodeIdentifiers) # set general linear map coefficients radiansAround = va * radiansPerElementAround radiansAroundNext = vb * radiansPerElementAround scalefactors = [ -1.0, math.cos(radiansAround), math.sin(radiansAround), radiansPerElementAround, math.cos(radiansAroundNext), math.sin(radiansAroundNext), radiansPerElementAround, math.cos(radiansAround), math.sin(radiansAround), radiansPerElementAround, math.cos(radiansAroundNext), math.sin(radiansAroundNext), radiansPerElementAround, math.cos(radiansIncline), math.sin(radiansIncline), math.cos(radiansInclineNext), math.sin(radiansInclineNext) ] result2 = element.setScaleFactors(eft3, scalefactors) # print('Tetrahedron top element', elementIdentifier, result1, result2, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 else: # Create pyramid elements on the top pole bni5 = elementsCountUp + 1 + (e3 - 1) * no3 + ( elementsCountUp - 2) * no2 + 1 for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eft5 = tricubichermite.createEftPyramidTop( va * 100, vb * 100, 100000 + e3 * 2) elementtemplate5.defineField(coordinates, -1, eft5) element = mesh.createElement(elementIdentifier, elementtemplate5) nodeIdentifiers = [ bni5 + va, bni5 + vb, elementsCountUp + 1, bni5 + no3 + va, bni5 + no3 + vb ] result1 = element.setNodesByIdentifier( eft5, nodeIdentifiers) # set general linear map coefficients radiansAround = va * radiansPerElementAround radiansAroundNext = vb * radiansPerElementAround scalefactors = [ -1.0, math.cos(radiansAround), math.sin(radiansAround), radiansPerElementAround, math.cos(radiansAroundNext), math.sin(radiansAroundNext), radiansPerElementAround, math.cos(radiansIncline), math.sin(radiansIncline), math.cos(radiansInclineNext), math.sin(radiansInclineNext) ] result2 = element.setScaleFactors(eft5, scalefactors) # print('pyramid top element', elementIdentifier, result1, result2, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 fm.endChange() return []
def generateMesh(region, options): """ :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: None """ lvWallThickness = options['LV wall thickness'] baseHeight = options['Base height'] baseThickness = options['Base thickness'] lvOutletRadius = options['LV outlet inner diameter'] * 0.5 lvOutletWallThickness = options['LV outlet wall thickness'] rvOutletRadius = options['RV outlet inner diameter'] * 0.5 rvOutletWallThickness = options['RV outlet wall thickness'] outletElementLength = options['Outlet element length'] useCrossDerivatives = False # generate default heart ventricles model to add base plane to heartVentriclesOptions = MeshType_3d_heartventricles2.getDefaultOptions( ) for key in [ 'LV wall thickness', 'LV wall thickness ratio apex', 'LV wall thickness ratio base', 'RV free wall thickness', 'RV width', 'Length ratio', 'Element length ratio equator/apex' ]: heartVentriclesOptions[key] = options[key] MeshType_3d_heartventricles2.generateMesh(region, heartVentriclesOptions) fm = region.getFieldmodule() fm.beginChange() # find the coordinates field created for the sphere shell coordinates = fm.findFieldByName('coordinates').castFiniteElement() nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) nodetemplateFull = nodes.createNodetemplate() nodetemplateFull.defineField(coordinates) nodetemplateFull.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplateFull.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplateFull.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) nodetemplateFull.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) # nodes used only in bicubic-linear elements do not have D_DS3 parameters 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) mesh = fm.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate.defineField(coordinates, -1, eft) cache = fm.createFieldcache() # create nodes nodeIdentifier = startNodeIdentifier = getMaximumNodeIdentifier( nodes) + 1 outletJoinRadians = math.pi # *0.8 cosOutletJoinRadians = math.cos(outletJoinRadians) sinOutletJoinRadians = math.sin(outletJoinRadians) lvOutletOffset = 0.5 - lvWallThickness - lvOutletRadius #lvOutletCentreX = cosOutletJoinRadians*lvOutletOffset #lvOutletCentreY = sinOutletJoinRadians*lvOutletOffset lvOutletCentreX = 0.0 lvOutletCentreY = lvOutletOffset # LV outlet - for bicubic-linear tube connection elementsCountAround = 6 radiansPerElementAround = 2.0 * math.pi / elementsCountAround x = [0.0, 0.0, baseHeight + baseThickness] dx_ds1 = [0.0, 0.0, 0.0] dx_ds2 = [0.0, 0.0, outletElementLength] dx_ds3 = [0.0, 0.0, 0.0] zero = [0.0, 0.0, 0.0] for n3 in range(2): radius = lvOutletRadius + lvOutletWallThickness * n3 for n1 in range(elementsCountAround): radiansAround = outletJoinRadians - math.pi + n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x[0] = lvOutletCentreX + radius * cosRadiansAround x[1] = lvOutletCentreY + radius * sinRadiansAround dx_ds1[ 0] = radiansPerElementAround * radius * -sinRadiansAround dx_ds1[1] = radiansPerElementAround * radius * cosRadiansAround nodetemplate = nodetemplateLinearS3 if ( n3 == 0) else nodetemplateFull node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) if nodetemplate is nodetemplateFull: dx_ds3[0] = lvOutletWallThickness * cosRadiansAround dx_ds3[1] = lvOutletWallThickness * sinRadiansAround coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) nodeIdentifier += 1 # RV outlet - for bicubic-linear tube connection elementsCountAround = 6 radiansPerElementAround = 2.0 * math.pi / elementsCountAround rvOutletOffset = lvOutletRadius + lvOutletWallThickness + rvOutletWallThickness + rvOutletRadius rvOutletCentreX = lvOutletCentreX + cosOutletJoinRadians * rvOutletOffset rvOutletCentreY = lvOutletCentreY + sinOutletJoinRadians * rvOutletOffset x = [0.0, 0.0, baseHeight + baseThickness] dx_ds1 = [0.0, 0.0, 0.0] dx_ds2 = [0.0, 0.0, outletElementLength] dx_ds3 = [0.0, 0.0, 0.0] for n3 in range(2): radius = rvOutletRadius + rvOutletWallThickness * n3 for n1 in range(elementsCountAround): if (n3 == 1) and (n1 == 0): continue # node is common with LV outlet radiansAround = outletJoinRadians - math.pi + n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x[0] = rvOutletCentreX + radius * cosRadiansAround x[1] = rvOutletCentreY + radius * sinRadiansAround dx_ds1[ 0] = radiansPerElementAround * radius * -sinRadiansAround dx_ds1[1] = radiansPerElementAround * radius * cosRadiansAround node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) nodeIdentifier += 1 # create elements elementIdentifier = startElementIdentifier = getMaximumElementIdentifier( mesh) + 1 rvNids = [[104, 105, 110, 111], [105, 106, 111, 112], [106, 107, 112, 113], [107, 108, 113, 114], [116, 117, 113, 122], [117, 118, 122, 123], [118, 119, 123, 124], [119, 120, 124, 125]] scalefactors5hanging = [-1.0, 0.5, 0.25, 0.125, 0.75] i = -1 for eid in range(55, 59): i += 1 if i < 2: continue origElement = mesh.findElementByIdentifier(eid) origEft = origElement.getElementfieldtemplate(coordinates, -1) origNodeIdentifiers = getElementNodeIdentifiers( origElement, origEft) # first and last elements have scaling to use and fix (GRC to do) if False: #eid == 55: eft1 = origEft else: eft1 = tricubichermite.createEftBasic() if False: #eid == 58: eft2 = origEft else: eft2 = tricubichermite.createEftBasic() # general scale factors 1 -> 1, 102 -> 1/2, 104 -> 1/4, 108 -> 1/8, 304 -> 3/4 setEftScaleFactorIds(eft1, [1, 102, 104, 108, 304], []) setEftScaleFactorIds(eft2, [1, 102, 104, 108, 304], []) tricubichermite.setEftMidsideXi1HangingNode( eft1, 2, 1, 1, 2, [1, 2, 3, 4, 5]) tricubichermite.setEftMidsideXi1HangingNode( eft1, 6, 5, 5, 6, [1, 2, 3, 4, 5]) tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1) tricubichermite.setEftMidsideXi1HangingNode( eft2, 1, 2, 1, 2, [1, 2, 3, 4, 5]) tricubichermite.setEftMidsideXi1HangingNode( eft2, 5, 6, 5, 6, [1, 2, 3, 4, 5]) tricubichermite.setEftLinearDerivativeXi3(eft2, 3, 7, 3, 7, 1) tricubichermite.setEftLinearDerivativeXi3(eft2, 4, 8, 4, 8, 1) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nids = rvNids[i * 2] nodeIdentifiers1 = [ origNodeIdentifiers[2], origNodeIdentifiers[3], nids[0], nids[1], origNodeIdentifiers[6], origNodeIdentifiers[7], nids[2], nids[3] ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, scalefactors5hanging) print('create hanging element 1', elementIdentifier, result2, result3, nodeIdentifiers1, scalefactors5hanging) elementIdentifier += 1 elementtemplate2 = mesh.createElementtemplate() elementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate2.defineField(coordinates, -1, eft2) element = mesh.createElement(elementIdentifier, elementtemplate2) nids = rvNids[i * 2 + 1] nodeIdentifiers2 = [ origNodeIdentifiers[2], origNodeIdentifiers[3], nids[0], nids[1], origNodeIdentifiers[6], origNodeIdentifiers[7], nids[2], nids[3] ] result2 = element.setNodesByIdentifier(eft2, nodeIdentifiers2) result3 = element.setScaleFactors(eft2, scalefactors5hanging) print('create hanging element 2', elementIdentifier, result2, result3, nodeIdentifiers2, scalefactors5hanging) elementIdentifier += 1 fm.endChange()
def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap, forceStartLinearXi3 = False, forceMidLinearXi3 = False, forceEndLinearXi3 = False, maxStartThickness = None, maxEndThickness = None, useCrossDerivatives = False, elementsCountRadial = 1, meshGroups = []): ''' Create an annulus mesh from a loop of start points/nodes with specified derivative mappings to a loop of end points/nodes with specified derivative mappings. Derivative d3 is through the wall. Currently limited to single element layer through wall. Points/nodes order cycles fastest around the annulus, then through the wall. Note doesn't support cross derivatives. Arrays are indexed by n3 (node through wall, size 2), n2 (node along/radial), n1 (node around, variable size) and coordinate component c. :param nodes: The nodeset to create nodes in. :param mesh: The mesh to create elements in. :param nextNodeIdentifier, nextElementIdentifier: Next identifiers to use and increment. :param startPointsx, startPointsd1, startPointsd2, startPointsd3, endPointsx, endPointsd1, endPointsd2, endPointsd3: List array[n3][n1][c] or start/point coordinates and derivatives. To linearise through the wall, pass None to d3. If both ends are linear through the wall, interior points are linear through the wall. :param startNodeId, endNodeId: List array [n3][n1] of existing node identifiers to use at start/end. Pass None for argument if no nodes are specified at end. These arguments are 'all or nothing'. :param startDerivativesMap, endDerivativesMap: List array[n3][n1] of mappings for d/dxi1, d/dxi2, d/dxi3 at start/end of form: ( (1, -1, 0), (1, 0, 0), None ) where the first tuple means d/dxi1 = d/ds1 - d/ds2. Only 0, 1 and -1 may be used. None means use default e.g. d/dxi2 = d/ds2. Pass None for the entire argument to use the defaults d/dxi1 = d/ds1, d/dxi2 = d/ds2, d/dxi3 = d/ds3. Pass a 4th mapping to apply to d/dxi1 on other side of node; if not supplied first mapping applies both sides. :param nodetemplate: Full tricubic Hermite node template, can omit cross derivatives. :param forceStartLinearXi3, forceMidLinearXi3, forceEndLinearXi3: Force start, middle or end elements to be linear through the wall, even if d3 is supplied at either end. Can only use forceMidLinearXi3 only if at least one end is linear in d3. :param maxStartThickness, maxEndThickness: Optional maximum override on start/end thicknesses. :param useCrossDerivatives: May only be True if no derivatives maps are in use. :param elementsCountRadial: Optional number of elements in radial direction between start and end. :param meshGroups: Optional list of Zinc MeshGroup for adding new elements to. :return: Final values of nextNodeIdentifier, nextElementIdentifier ''' assert (elementsCountRadial >= 1), 'createAnnulusMesh3d: Invalid number of radial elements' startLinearXi3 = (not startPointsd3) or forceStartLinearXi3 endLinearXi3 = (not endPointsd3) or forceEndLinearXi3 midLinearXi3 = (startLinearXi3 and endLinearXi3) or ((startLinearXi3 or endLinearXi3) and forceMidLinearXi3) # get list whether each row of nodes in elements is linear in Xi3 # this is for element use; start/end nodes may have d3 even if element is linear rowLinearXi3 = [ startLinearXi3 ] + [ midLinearXi3 ]*(elementsCountRadial - 1) + [ endLinearXi3 ] assert (not useCrossDerivatives) or ((not startDerivativesMap) and (not endDerivativesMap)), \ 'createAnnulusMesh3d: Cannot use cross derivatives with derivatives map' elementsCountWall = 1 nodesCountWall = elementsCountWall + 1 assert (len(startPointsx) == nodesCountWall) and (len(startPointsd1) == nodesCountWall) and (len(startPointsd2) == nodesCountWall) and \ (startLinearXi3 or (len(startPointsd3) == nodesCountWall)) and \ (len(endPointsx) == nodesCountWall) and (len(endPointsd1) == nodesCountWall) and (len(endPointsd2) == nodesCountWall) and \ (endLinearXi3 or (len(endPointsd3) == nodesCountWall)) and \ ((startNodeId is None) or (len(startNodeId) == nodesCountWall)) and \ ((endNodeId is None) or (len(endNodeId) == nodesCountWall)) and \ ((startDerivativesMap is None) or (len(startDerivativesMap) == nodesCountWall)) and \ ((endDerivativesMap is None) or (len(endDerivativesMap) == nodesCountWall)), \ 'createAnnulusMesh3d: Mismatch in number of layers through wall' elementsCountAround = nodesCountAround = len(startPointsx[0]) assert (nodesCountAround > 1), 'createAnnulusMesh3d: Invalid number of points/nodes around annulus' for n3 in range(nodesCountWall): assert (len(startPointsx[n3]) == nodesCountAround) and (len(startPointsd1[n3]) == nodesCountAround) and (len(startPointsd2[n3]) == nodesCountAround) and \ (startLinearXi3 or (len(startPointsd3[n3]) == nodesCountAround)) and \ (len(endPointsx[n3]) == nodesCountAround) and (len(endPointsd1[n3]) == nodesCountAround) and (len(endPointsd2[n3]) == nodesCountAround) and \ (endLinearXi3 or (len(endPointsd3[n3]) == nodesCountAround)) and \ ((startNodeId is None) or (len(startNodeId[n3]) == nodesCountAround)) and \ ((endNodeId is None) or (len(endNodeId[n3]) == nodesCountAround)) and \ ((startDerivativesMap is None) or (len(startDerivativesMap[n3]) == nodesCountAround)) and \ ((endDerivativesMap is None) or (len(endDerivativesMap[n3]) == nodesCountAround)), \ 'createAnnulusMesh3d: Mismatch in number of points/nodes in layers through wall' fm = mesh.getFieldmodule() fm.beginChange() cache = fm.createFieldcache() coordinates = zinc_utils.getOrCreateCoordinateField(fm) # Build arrays of points from start to end px = [ [], [] ] pd1 = [ [], [] ] pd2 = [ [], [] ] pd3 = [ [], [] ] for n3 in range(2): px [n3] = [ startPointsx [n3], endPointsx [n3] ] pd1[n3] = [ startPointsd1[n3], endPointsd1[n3] ] pd2[n3] = [ startPointsd2[n3], endPointsd2[n3] ] pd3[n3] = [ startPointsd3[n3] if (startPointsd3 is not None) else None, \ endPointsd3[n3] if (endPointsd3 is not None) else None ] if elementsCountRadial > 1: # add in-between points startPointsd = [ startPointsd1, startPointsd2, startPointsd3 ] startPointsdslimit = 2 if (startPointsd3 is None) else 3 endPointsd = [ endPointsd1, endPointsd2, endPointsd3 ] endPointsdslimit = 2 if (endPointsd3 is None) else 3 for n3 in range(2): for n2 in range(1, elementsCountRadial): px [n3].insert(n2, [ None ]*nodesCountAround) pd1[n3].insert(n2, [ None ]*nodesCountAround) pd2[n3].insert(n2, [ None ]*nodesCountAround) pd3[n3].insert(n2, None if midLinearXi3 else [ None ]*nodesCountAround) # compute on outside / n3 = 1, then map to inside using thickness thicknesses = [] thicknesses.append([ vector.magnitude([ (startPointsx[1][n1][c] - startPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) if maxStartThickness: for n1 in range(nodesCountAround): thicknesses[0][n1] = min(thicknesses[0][n1], maxStartThickness) for n2 in range(1, elementsCountRadial): thicknesses.append([ None ]*nodesCountAround) thicknesses.append([ vector.magnitude([ (endPointsx[1][n1][c] - endPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) if maxEndThickness: for n1 in range(nodesCountAround): thicknesses[-1][n1] = min(thicknesses[-1][n1], maxEndThickness) n3 == 1 for n1 in range(nodesCountAround): ax = startPointsx [n3][n1] if (startDerivativesMap is None) or (startDerivativesMap[n3][n1][0] is None): ad1 = startPointsd1[n3][n1] else: derivativesMap = startDerivativesMap[n3][n1][0] ad1 = [ 0.0, 0.0, 0.0 ] for ds in range(startPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): ad1[c] += derivativesMap[ds]*startPointsd[ds][n3][n1][c] if len(startDerivativesMap[n3][n1]) > 3: # average with d1 map for other side derivativesMap = startDerivativesMap[n3][n1][3] ad1 = [ 0.5*d for d in ad1 ] if not derivativesMap: for c in range(3): ad1[c] += 0.5*startPointsd[0][n3][n1][c] else: for ds in range(startPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): ad1[c] += 0.5*derivativesMap[ds]*startPointsd[ds][n3][n1][c] if (startDerivativesMap is None) or (startDerivativesMap[n3][n1][1] is None): ad2 = startPointsd2[n3][n1] else: derivativesMap = startDerivativesMap[n3][n1][1] ad2 = [ 0.0, 0.0, 0.0 ] for ds in range(startPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): ad2[c] += derivativesMap[ds]*startPointsd[ds][n3][n1][c] bx = endPointsx [n3][n1] if (endDerivativesMap is None) or (endDerivativesMap[n3][n1][0] is None): bd1 = endPointsd1[n3][n1] else: derivativesMap = endDerivativesMap[n3][n1][0] bd1 = [ 0.0, 0.0, 0.0 ] for ds in range(endPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): bd1[c] += derivativesMap[ds]*endPointsd[ds][n3][n1][c] if len(endDerivativesMap[n3][n1]) > 3: # average with d1 map for other side derivativesMap = endDerivativesMap[n3][n1][3] bd1 = [ 0.5*d for d in bd1 ] if not derivativesMap: for c in range(3): bd1[c] += 0.5*endPointsd[0][n3][n1][c] else: for ds in range(endPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): bd1[c] += 0.5*derivativesMap[ds]*endPointsd[ds][n3][n1][c] if (endDerivativesMap is None) or (endDerivativesMap[n3][n1][1] is None): bd2 = endPointsd2[n3][n1] else: derivativesMap = endDerivativesMap[n3][n1][1] bd2 = [ 0.0, 0.0, 0.0 ] for ds in range(endPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): bd2[c] += derivativesMap[ds]*endPointsd[ds][n3][n1][c] # scaling end derivatives to arc length gives even curvature along the curve arcLength = interp.computeCubicHermiteArcLength(ax, ad2, bx, bd2, rescaleDerivatives = False) scaledDerivatives = [ vector.setMagnitude(d2, arcLength) for d2 in [ ad2, bd2 ]] mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth([ ax, bx ], scaledDerivatives, elementsCountRadial, derivativeMagnitudeStart = vector.magnitude(ad2), derivativeMagnitudeEnd = vector.magnitude(bd2))[0:4] md1 = interp.interpolateSampleLinear([ ad1, bd1 ], me, mxi) thi = interp.interpolateSampleLinear([ thicknesses[0][n1], thicknesses[-1][n1] ], me, mxi) #md2 = interp.smoothCubicHermiteDerivativesLine(mx, md2, fixStartDerivative = True, fixEndDerivative = True) for n2 in range(1, elementsCountRadial): px [n3][n2][n1] = mx [n2] pd1[n3][n2][n1] = md1[n2] pd2[n3][n2][n1] = md2[n2] thicknesses[n2][n1] = thi[n2] # now get inner positions from normal and thickness, derivatives from curvature for n2 in range(1, elementsCountRadial): # first smooth derivative 1 around outer loop pd1[1][n2] = interp.smoothCubicHermiteDerivativesLoop(px[1][n2], pd1[1][n2], magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) for n1 in range(nodesCountAround): normal = vector.normalise(vector.crossproduct3(pd1[1][n2][n1], pd2[1][n2][n1])) thickness = thicknesses[n2][n1] d3 = [ d*thickness for d in normal ] px [0][n2][n1] = [ (px [1][n2][n1][c] - d3[c]) for c in range(3) ] # calculate inner d1 from curvature around n1m = n1 - 1 n1p = (n1 + 1)%nodesCountAround curvature = 0.5*( interp.getCubicHermiteCurvature(px[1][n2][n1m], pd1[1][n2][n1m], px[1][n2][n1 ], pd1[1][n2][n1 ], normal, 1.0) + interp.getCubicHermiteCurvature(px[1][n2][n1 ], pd1[1][n2][n1 ], px[1][n2][n1p], pd1[1][n2][n1p], normal, 0.0)) factor = 1.0 + curvature*thickness pd1[0][n2][n1] = [ factor*d for d in pd1[1][n2][n1] ] # calculate inner d2 from curvature radially n2m = n2 - 1 n2p = n2 + 1 curvature = 0.5*( interp.getCubicHermiteCurvature(px[1][n2m][n1], pd2[1][n2m][n1], px[1][n2 ][n1], pd2[1][n2 ][n1], normal, 1.0) + interp.getCubicHermiteCurvature(px[1][n2 ][n1], pd2[1][n2 ][n1], px[1][n2p][n1], pd2[1][n2p][n1], normal, 0.0)) factor = 1.0 + curvature*thickness pd2[0][n2][n1] = [ factor*d for d in pd2[1][n2][n1] ] if not midLinearXi3: pd3[0][n2][n1] = pd3[1][n2][n1] = d3 # smooth derivative 1 around inner loop pd1[0][n2] = interp.smoothCubicHermiteDerivativesLoop(px[0][n2], pd1[0][n2], magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) for n3 in range(0, 1): # was (0, nodesCountWall) # smooth derivative 2 radially/along annulus for n1 in range(nodesCountAround): sd2 = interp.smoothCubicHermiteDerivativesLine( [ px [n3][n2][n1] for n2 in range(elementsCountRadial + 1) ], [ pd2[n3][n2][n1] for n2 in range(elementsCountRadial + 1) ], fixAllDirections = True, fixStartDerivative = True, fixEndDerivative = True, magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) for n2 in range(elementsCountRadial + 1): pd2[n3][n2][n1] = sd2[n2] ############## # Create nodes ############## nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCrossDerivatives: nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) if useCrossDerivatives: nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS3, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS2DS3, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1) nodetemplateLinearS3 = nodes.createNodetemplate() nodetemplateLinearS3.defineField(coordinates) nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCrossDerivatives: nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodeIdentifier = nextNodeIdentifier nodeId = [ [], [] ] for n3 in range(2): for n2 in range(elementsCountRadial + 1): if (n2 == 0) and (startNodeId is not None): rowNodeId = copy.deepcopy(startNodeId[n3]) elif (n2 == elementsCountRadial) and (endNodeId is not None): rowNodeId = copy.deepcopy(endNodeId[n3]) else: rowNodeId = [] nodetemplate1 = nodetemplate if pd3[n3][n2] else nodetemplateLinearS3 for n1 in range(nodesCountAround): node = nodes.createNode(nodeIdentifier, nodetemplate1) rowNodeId.append(nodeIdentifier) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, px[n3][n2][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, pd1[n3][n2][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, pd2[n3][n2][n1]) if pd3[n3][n2]: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, pd3[n3][n2][n1]) nodeIdentifier = nodeIdentifier + 1 nodeId[n3].append(rowNodeId) ################# # Create elements ################# tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) bicubichermitelinear = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives) elementIdentifier = nextElementIdentifier elementtemplateStandard = mesh.createElementtemplate() elementtemplateStandard.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplateX = mesh.createElementtemplate() elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) for e2 in range(elementsCountRadial): nonlinearXi3 = (not rowLinearXi3[e2]) or (not rowLinearXi3[e2 + 1]) eftFactory = tricubichermite if nonlinearXi3 else bicubichermitelinear eftStandard = eftFactory.createEftBasic() elementtemplateStandard.defineField(coordinates, -1, eftStandard) mapStartDerivatives = (e2 == 0) and (startDerivativesMap is not None) mapStartLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2] mapEndDerivatives = (e2 == (elementsCountRadial - 1)) and (endDerivativesMap is not None) mapEndLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2 + 1] mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or mapEndDerivatives or mapEndLinearDerivativeXi3 for e1 in range(elementsCountAround): en = (e1 + 1)%elementsCountAround nids = [ nodeId[0][e2][e1], nodeId[0][e2][en], nodeId[0][e2 + 1][e1], nodeId[0][e2 + 1][en], nodeId[1][e2][e1], nodeId[1][e2][en], nodeId[1][e2 + 1][e1], nodeId[1][e2 + 1][en] ] if mapDerivatives: eft1 = eftFactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) if mapStartLinearDerivativeXi3: eftFactory.setEftLinearDerivative(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS3, 1, 5, 1) eftFactory.setEftLinearDerivative(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS3, 2, 6, 1) if mapStartDerivatives: for i in range(2): lns = [ 1, 5 ] if (i == 0) else [ 2, 6 ] for n3 in range(2): derivativesMap = startDerivativesMap[n3][e1] if (i == 0) else startDerivativesMap[n3][en] # handle different d1 on each side of node d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] d2Map = derivativesMap[1] d3Map = derivativesMap[2] # use temporary to safely swap DS1 and DS2: ln = [ lns[n3] ] if d1Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ]) if d3Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ]) if d2Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d2Map)) if d1Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d1Map)) if d3Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d3Map)) if mapEndLinearDerivativeXi3: eftFactory.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) eftFactory.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) if mapEndDerivatives: for i in range(2): lns = [ 3, 7 ] if (i == 0) else [ 4, 8 ] for n3 in range(2): derivativesMap = endDerivativesMap[n3][e1] if (i == 0) else endDerivativesMap[n3][en] # handle different d1 on each side of node d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] d2Map = derivativesMap[1] d3Map = derivativesMap[2] # use temporary to safely swap DS1 and DS2: ln = [ lns[n3] ] if d1Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ]) if d3Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ]) if d2Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d2Map)) if d1Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d1Map)) if d3Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d3Map)) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX else: eft1 = eftStandard elementtemplate1 = elementtemplateStandard element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) if mapDerivatives: result3 = element.setScaleFactors(eft1, [ -1.0 ]) #else: # result3 = '-' #print('create element annulus', element.isValid(), elementIdentifier, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) fm.endChange() return nodeIdentifier, elementIdentifier
def 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: [] empty list of AnnotationGroup """ elementsCountAlong = options['Number of elements along'] elementsCountAcross = options['Number of elements across'] wallThickness = [ options['Wall thickness left'], options['Wall thickness right'] ] flangeLength = options['Flange length'] bulgeRadius = options['Bulge radius'] useCrossDerivatives = options['Use cross derivatives'] fm = region.getFieldmodule() fm.beginChange() coordinates = findOrCreateFieldCoordinates(fm) 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) 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) mesh = fm.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftBasic() eftOuter = tricubichermite.createEftTubeSeptumOuter() eftInner1 = tricubichermite.createEftTubeSeptumInner1() eftInner2 = tricubichermite.createEftTubeSeptumInner2() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate.defineField(coordinates, -1, eft) elementtemplateOuter = mesh.createElementtemplate() elementtemplateOuter.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplateOuter.defineField(coordinates, -1, eftOuter) elementtemplateInner1 = mesh.createElementtemplate() elementtemplateInner1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplateInner1.defineField(coordinates, -1, eftInner1) elementtemplateInner2 = mesh.createElementtemplate() elementtemplateInner2.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplateInner2.defineField(coordinates, -1, eftInner2) cache = fm.createFieldcache() # create nodes nodeIdentifier = 1 #radiansPerElementAcross = math.pi/elementsCountAcross bevelAngle = math.pi/2 sinBevelAngle = math.sin(bevelAngle) cosBevelAngle = math.cos(bevelAngle) x = [ 0.0, 0.0, 0.0 ] dx_ds1 = [ 0.0, 0.0, 0.0 ] dx_ds2 = [ 0.0, 0.0, 1.0 / elementsCountAlong ] dx_ds3 = [ 0.0, 0.0, 0.0 ] zero = [ 0.0, 0.0, 0.0 ] wallThicknessSeptum = wallThickness[0] #wallThicknessMin = min(wallThickness) wallThicknessMax = max(wallThickness) for n3 in range(2): sign = -1.0 if (n3 == 0) else 1.0 radiusY = 0.5*wallThicknessSeptum radiusX = 0.5 - wallThicknessMax for n2 in range(elementsCountAlong + 1): x[2] = n2 / elementsCountAlong for n1 in range(elementsCountAcross + 3): if (n1 == 0) or (n1 == (elementsCountAcross + 2)): flip = -1.0 if (n1 == 0) else 1.0 x[0] = radiusX + wallThickness[n3] if n1 == 0: x[0] = -x[0] x[1] = -sign*(radiusY + flangeLength) #if wallThickness[0] > wallThickness[1]: # x[1] -= flangeLength #elif wallThickness[0] < wallThickness[1]: # x[1] += flangeLength dx_ds1[0] = 0.0 dx_ds1[1] = 2.0*flip*(radiusY + flangeLength) dx_ds3[0] = flip*wallThickness[n3] dx_ds3[1] = 0.0 elif (n1 == 1) or (n1 == (elementsCountAcross + 1)): flip = -1.0 if (n1 == 1) else 1.0 radius = radiusX x[0] = flip*radius x[1] = -sign*(radiusY + flangeLength) #dx_ds1[0] = -sign*2.0*radiusX/elementsCountAcross*cosBevelAngle #dx_ds1[1] = 2.0*radiusX/elementsCountAcross*sinBevelAngle mag1x = radiusX/elementsCountAcross mag1y = flangeLength #mag1min = radiusX/elementsCountAcross + flangeLength + math.sqrt(mag1x*mag1x + mag1y*mag1y) mag1min = 2.0*math.sqrt(mag1x*mag1x + mag1y*mag1y) mag1max = 2.0*(radiusY + flangeLength) mag1 = mag1min if (mag1min < mag1max) else mag1max dx_ds1[0] = -sign*mag1*cosBevelAngle dx_ds1[1] = mag1*sinBevelAngle #if ((n3 == 1) and (wallThickness[0] > wallThickness[1])) or \ # ((n3 == 0) and (wallThickness[0] < wallThickness[1])): dx_ds3[0] = wallThickness[n3] dx_ds3[1] = 0.0 #else: # dx_ds3[0] = wallThickness[n3]*sinBevelAngle # dx_ds3[1] = sign*wallThickness[n3]*cosBevelAngle if n1 == 1: dx_ds1[1] = -dx_ds1[1] dx_ds3[0] = -dx_ds3[0] else: f1 = (n1 - 1)/elementsCountAcross f2 = 1.0 - f1 x[0] = (f1 - f2)*radiusX x[1] = -sign*radiusY dx_ds1[0] = 2.0*radiusX/elementsCountAcross dx_ds1[1] = 0.0 dx_ds3[0] = 0.0 dx_ds3[1] = -wallThicknessSeptum node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) if useCrossDerivatives: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 rimCrossAngle = math.pi/4 sinRimCrossAngle = math.sin(rimCrossAngle) cosRimCrossAngle = math.cos(rimCrossAngle) scaleFactorsOuter = [ cosRimCrossAngle, sinRimCrossAngle, cosRimCrossAngle, -sinRimCrossAngle, cosRimCrossAngle, sinRimCrossAngle, cosRimCrossAngle, -sinRimCrossAngle ] scaleFactorsInner1 = [ -1.0, 4.0, cosRimCrossAngle, sinRimCrossAngle, cosRimCrossAngle, sinRimCrossAngle, cosRimCrossAngle, -sinRimCrossAngle, cosRimCrossAngle, -sinRimCrossAngle ] scaleFactorsInner2 = [ -1.0, 4.0, cosRimCrossAngle, -sinRimCrossAngle, cosRimCrossAngle, -sinRimCrossAngle, cosRimCrossAngle, sinRimCrossAngle, cosRimCrossAngle, sinRimCrossAngle ] # create elements elementIdentifier = 1 rno = elementsCountAcross + 3 wno = (elementsCountAlong + 1)*rno for e2 in range(elementsCountAlong): bn = e2*rno element = mesh.createElement(elementIdentifier, elementtemplateOuter) bni11 = bn + 2 bni12 = bn + wno + 2 bni21 = bn + rno + 2 bni22 = bn + wno + rno + 2 nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 - 1, bni12 - 1, bni21 - 1, bni22 - 1 ] result = element.setNodesByIdentifier(eftOuter, nodeIdentifiers) #print(result, 'element', elementIdentifier, nodeIdentifiers) element.setScaleFactors(eftOuter, scaleFactorsOuter) elementIdentifier = elementIdentifier + 1 element = mesh.createElement(elementIdentifier, elementtemplateInner1) bni11 = bn + 2 bni12 = bn + 3 bni21 = bn + rno + 2 bni22 = bn + rno + 3 nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + wno, bni12 + wno, bni21 + wno, bni22 + wno ] result = element.setNodesByIdentifier(eftInner1, nodeIdentifiers) #print(result, 'element', elementIdentifier, 'nodes', nodeIdentifiers) element.setScaleFactors(eftInner1, scaleFactorsInner1) #print(result, 'element', elementIdentifier, 'scale factors', scaleFactorsInner1) elementIdentifier = elementIdentifier + 1 for e1 in range(elementsCountAcross - 2): element = mesh.createElement(elementIdentifier, elementtemplate) bni11 = bn + e1 + 3 bni12 = bn + e1 + 4 bni21 = bn + rno + e1 + 3 bni22 = bn + rno + e1 + 4 nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + wno, bni12 + wno, bni21 + wno, bni22 + wno ] result = element.setNodesByIdentifier(eft, nodeIdentifiers) #print(result, 'element', elementIdentifier, 'nodes', nodeIdentifiers) elementIdentifier = elementIdentifier + 1 element = mesh.createElement(elementIdentifier, elementtemplateInner2) bni11 = bn + rno - 2 bni12 = bn + rno - 1 bni21 = bn + 2*rno -2 bni22 = bn + 2*rno - 1 nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + wno, bni12 + wno, bni21 + wno, bni22 + wno ] result = element.setNodesByIdentifier(eftInner2, nodeIdentifiers) #print(result, 'element', elementIdentifier, 'nodes', nodeIdentifiers) element.setScaleFactors(eftInner2, scaleFactorsInner2) #print(result, 'element', elementIdentifier, 'scale factors', scaleFactorsInner1) elementIdentifier = elementIdentifier + 1 element = mesh.createElement(elementIdentifier, elementtemplateOuter) bni11 = bn + wno + rno - 1 bni12 = bn + rno - 1 bni21 = bn + wno + 2*rno - 1 bni22 = bn + 2*rno - 1 nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + 1, bni12 + 1, bni21 + 1, bni22 + 1 ] result = element.setNodesByIdentifier(eftOuter, nodeIdentifiers) #print(result, 'element', elementIdentifier, nodeIdentifiers) element.setScaleFactors(eftOuter, scaleFactorsOuter) elementIdentifier = elementIdentifier + 1 if bulgeRadius != 0.0: # cylindrical polar coordinates: # r = y - bulgeRadius # theta = -x / bulgeRadius # z = z yxzCoordinates = fm.createFieldComponent(coordinates, [2, 1, 3]) scale = fm.createFieldConstant([1.0, -1.0/bulgeRadius, 1.0 ]) scaleCoordinates = fm.createFieldMultiply(yxzCoordinates, scale) offset = fm.createFieldConstant([-bulgeRadius, 0.0, 0.0 ]) polarCoordinates = fm.createFieldAdd(scaleCoordinates, offset) polarCoordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_CYLINDRICAL_POLAR) rcCoordinates = fm.createFieldCoordinateTransformation(polarCoordinates) rcCoordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) newyxzCoordinates = fm.createFieldSubtract(rcCoordinates, offset) newCoordinates = fm.createFieldComponent(newyxzCoordinates, [2, 1, 3]) fieldassignment = coordinates.createFieldassignment(newCoordinates) result = fieldassignment.assign() fm.endChange() return []
def generateBaseMesh(region, options): """ Generate the base tricubic Hermite mesh. See also generateMesh(). :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: None """ elementsCountAround = options['Number of elements around'] elementsCountUp = options['Number of elements up'] elementsCountThroughLVWall = options['Number of elements through LV wall'] elementsCountAcrossSeptum = options['Number of elements across septum'] elementsCountBelowSeptum = options['Number of elements below septum'] LVWallThickness = options['LV wall thickness'] LVWallThicknessRatioApex = options['LV wall thickness ratio apex'] LVWallThicknessRatioBase = options['LV wall thickness ratio base'] LVBaseFlattenRatio = options['LV base flatten ratio'] LVBaseFlattenAngleRadians = options['LV base flatten angle degrees']*math.pi/180.0 lengthRatio = options['Length ratio'] RVWidthTop = options['RV width'] RVFreeWallThickness = options['RV free wall thickness'] septumArcAngleRadians = options['Septum arc angle degrees']*math.pi/180.0 useCrossDerivatives = options['Use cross derivatives'] # generate a half sphere shell which will be edited sphereShellOptions = MeshType_3d_sphereshell1.getDefaultOptions() sphereShellOptions['Number of elements up'] = elementsCountUp*2 sphereShellOptions['Number of elements around'] = elementsCountAround sphereShellOptions['Number of elements through wall'] = elementsCountThroughLVWall sphereShellOptions['Exclude top rows'] = elementsCountUp sphereShellOptions['Wall thickness'] = LVWallThickness sphereShellOptions['Wall thickness ratio apex'] = LVWallThicknessRatioApex sphereShellOptions['Length ratio'] = lengthRatio sphereShellOptions['Element length ratio equator/apex'] = options['Element length ratio equator/apex'] MeshType_3d_sphereshell1.generateBaseMesh(region, sphereShellOptions) fm = region.getFieldmodule() fm.beginChange() coordinates = getOrCreateCoordinateField(fm) 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) mesh = fm.findMeshByDimension(3) cache = fm.createFieldcache() nor = elementsCountAround now = 1 + elementsCountUp*nor # Resize elements around LV to get desired septum arc angle radiansPerElementOrig = 2.0*math.pi/elementsCountAround radiansPerElementSeptum = septumArcAngleRadians/elementsCountAcrossSeptum # want LV-RV 'transition' elements to be mean size of Septum and LV FreeWall elements radiansRemaining = 2.0*math.pi - septumArcAngleRadians elementsCountAroundLVFreeWall = elementsCountAround - elementsCountAcrossSeptum - 2 radiansPerElementLVFreeWall = (radiansRemaining - radiansPerElementSeptum) / (elementsCountAroundLVFreeWall + 1) radiansPerElementTransition = 0.5*(radiansPerElementSeptum + radiansPerElementLVFreeWall) #print('Element size ratio LVFreeWall / Septum', radiansPerElementLVFreeWall/radiansPerElementSeptum) xyz_scale = fm.createFieldConstant([1.0, 1.0, lengthRatio/2.0 - LVWallThickness*LVWallThicknessRatioApex]) coordinates_scale = fm.createFieldMultiply(coordinates, xyz_scale) sp = fm.createFieldCoordinateTransformation(coordinates_scale) sp.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_SPHERICAL_POLAR) r = fm.createFieldComponent(sp, 1) theta = fm.createFieldComponent(sp, 2) phi = fm.createFieldComponent(sp, 3) zero = fm.createFieldConstant([0.0]) one = fm.createFieldConstant([1.0]) # also used as 'true' two_pi = fm.createFieldConstant([2.0*math.pi]) radians_per_element_orig = fm.createFieldConstant([radiansPerElementOrig]) half_radians_per_element_orig = fm.createFieldConstant([0.5*radiansPerElementOrig]) radians_per_element_transition = fm.createFieldConstant([radiansPerElementTransition]) theta_continuous = fm.createFieldIf(fm.createFieldLessThan(theta, half_radians_per_element_orig), fm.createFieldAdd(theta, two_pi), theta) theta_offset = fm.createFieldSubtract(theta_continuous, radians_per_element_orig) theta_offset_septum_start = fm.createFieldConstant([-0.5*radiansPerElementOrig]) theta_offset_septum_end = fm.createFieldConstant([(elementsCountAcrossSeptum + 0.5)*radiansPerElementOrig]) in_septum = fm.createFieldAnd(fm.createFieldGreaterThan(theta_offset, theta_offset_septum_start), fm.createFieldLessThan(theta_offset, theta_offset_septum_end)) septum_scale = fm.createFieldConstant([radiansPerElementSeptum/radiansPerElementOrig]) thetaNewSeptumStart = radiansPerElementOrig + (radiansPerElementOrig - radiansPerElementSeptum)*elementsCountAcrossSeptum/2.0 theta_new_septum_start = fm.createFieldConstant([thetaNewSeptumStart]) theta_new_septum = fm.createFieldAdd(fm.createFieldMultiply(theta_offset, septum_scale), theta_new_septum_start) lvfreewall_scale = fm.createFieldConstant([radiansPerElementLVFreeWall/radiansPerElementOrig]) theta_offset_lvfreewall_start = fm.createFieldConstant([radiansPerElementOrig*(elementsCountAcrossSeptum + 1.0)]) theta_new_lvfreewall_start = fm.createFieldConstant([thetaNewSeptumStart + septumArcAngleRadians + radiansPerElementTransition]) theta_new_lvfreewall = fm.createFieldAdd( fm.createFieldMultiply(fm.createFieldSubtract(theta_offset, theta_offset_lvfreewall_start), lvfreewall_scale), theta_new_lvfreewall_start) theta_new = fm.createFieldIf(in_septum, theta_new_septum, theta_new_lvfreewall) sp_new = fm.createFieldConcatenate([r, theta_new, phi]) sp_new.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_SPHERICAL_POLAR) coordinates_new_scale = fm.createFieldCoordinateTransformation(sp_new) coordinates_new_scale.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) coordinates_new = fm.createFieldDivide(coordinates_new_scale, xyz_scale) nonApexNodesetGroupField = fm.createFieldNodeGroup(nodes) nonApexNodesetGroup = nonApexNodesetGroupField.getNodesetGroup() nonApexNodesetGroup.addNodesConditional(one) # add all # remove apex nodes: for i in range(elementsCountThroughLVWall + 1): node = nodes.findNodeByIdentifier(i*now + 1) nonApexNodesetGroup.removeNode(node) fieldassignment = coordinates.createFieldassignment(coordinates_new) fieldassignment.setNodeset(nonApexNodesetGroup) fieldassignment.assign() baseNodesetGroup = None baseNodeGroupField = fm.createFieldNodeGroup(nodes) z = fm.createFieldComponent(coordinates, 3) is_base = fm.createFieldGreaterThan(z, fm.createFieldConstant([-0.0001])) baseNodesetGroup = baseNodeGroupField.getNodesetGroup() baseNodesetGroup.addNodesConditional(is_base) #print('baseNodesetGroup.getSize()', baseNodesetGroup.getSize()) if LVWallThicknessRatioBase != 1.0: # make LV walls thinner at base # get inside node at middle of RV now = 1 + elementsCountUp*elementsCountAround midRVnid = now - elementsCountAround + 2 + (elementsCountAcrossSeptum // 2) #print('midRVnid', midRVnid) midRVnode = nodes.findNodeByIdentifier(midRVnid) cp_coordinates = fm.createFieldCoordinateTransformation(coordinates) cp_coordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_CYLINDRICAL_POLAR) radius = fm.createFieldComponent(cp_coordinates, 1) theta = fm.createFieldComponent(cp_coordinates, 2) z = fm.createFieldComponent(cp_coordinates, 3) cache.setNode(midRVnode) #result, cp = cp_coordinates.evaluateReal(cache, 3) #coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) result, innerRadius = radius.evaluateReal(cache, 1) #print('innerRadius', innerRadius) ir = fm.createFieldConstant([innerRadius*0.9999]) radius_gt_ir = fm.createFieldGreaterThan(radius, ir) radius_minus_ir = fm.createFieldSubtract(radius, ir) thickness_scale = fm.createFieldConstant([LVWallThicknessRatioBase]) delta_radius = fm.createFieldMultiply(radius_minus_ir, thickness_scale) new_radius = fm.createFieldAdd(ir, delta_radius) new_cp_coordinates = fm.createFieldConcatenate([new_radius, theta, z]) new_cp_coordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_CYLINDRICAL_POLAR) new_coordinates = fm.createFieldCoordinateTransformation(new_cp_coordinates) new_coordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) fieldassignment = coordinates.createFieldassignment(new_coordinates) result = fieldassignment.setNodeset(baseNodesetGroup) fieldassignment.assign() if LVBaseFlattenRatio != 1.0: # flatten LV normal to mid-RV-aorta-mitral axis plus additional flatten angle septumCentreRadians = (1.0 + elementsCountAcrossSeptum/2.0)*radiansPerElementOrig flattenAxisRadians = septumCentreRadians + LVBaseFlattenAngleRadians - 0.5*math.pi half = fm.createFieldConstant([0.5]) minus_one = fm.createFieldConstant([-1.0]) minus_two = fm.createFieldConstant([-2.0]) # flatten ratio = new inner radius / original inner radius beta_base = fm.createFieldConstant([1.0 - LVBaseFlattenRatio]) z = fm.createFieldComponent(coordinates, 3) z2 = fm.createFieldMultiply(z, z) zero_dist_node = nodes.findNodeByIdentifier(now - nor) cache.setNode(zero_dist_node) result, z_zero_dist_value = z.evaluateReal(cache, 1) #print('zero dist', result, z_zero_dist_value) z_zerodist = fm.createFieldConstant([z_zero_dist_value]) z_zerodist2 = fm.createFieldMultiply(z_zerodist, z_zerodist) z_a = fm.createFieldDivide(one, z_zerodist2) z_b = fm.createFieldDivide(minus_two, z_zerodist) zfact = fm.createFieldAdd(fm.createFieldAdd(fm.createFieldMultiply(z_a, z2), fm.createFieldMultiply(z_b, z)), one) beta = fm.createFieldMultiply(zfact, beta_base) # 1 - squash factor alpha = fm.createFieldSubtract(one, beta) # z-dependent squash factor psi = fm.createFieldConstant([flattenAxisRadians]) ri = fm.createFieldConstant([0.5 - LVWallThickness]) theta_minus_psi = fm.createFieldSubtract(theta, psi) cos_theta_minus_psi = fm.createFieldCos(theta_minus_psi) sin_theta_minus_psi = fm.createFieldSin(theta_minus_psi) r_minus_ri = fm.createFieldSubtract(r, ri) rf = fm.createFieldAdd(fm.createFieldMultiply(alpha, r), fm.createFieldMultiply(beta, r_minus_ri)) x_new = fm.createFieldMultiply(rf, cos_theta_minus_psi) y_new = fm.createFieldMultiply(r, sin_theta_minus_psi) r2_new = fm.createFieldAdd(fm.createFieldMultiply(x_new, x_new), fm.createFieldMultiply(y_new, y_new)) r_new = fm.createFieldSqrt(r2_new) theta_minus_psi_raw = fm.createFieldAtan2(y_new, x_new) theta_wrap = fm.createFieldAnd( fm.createFieldLessThan(theta_minus_psi, minus_one), fm.createFieldGreaterThan(theta_minus_psi_raw, one)) theta_minus_psi_fix = fm.createFieldIf(theta_wrap, fm.createFieldAdd(theta_minus_psi, two_pi), theta_minus_psi) # above theta is too great; average with theta_minus_psi_raw theta_minus_psi_new = fm.createFieldMultiply(half, fm.createFieldAdd(theta_minus_psi_fix, theta_minus_psi_raw)) theta_new = fm.createFieldAdd(theta_minus_psi_new, psi) sp_new = fm.createFieldConcatenate([r_new, theta_new, phi]) sp_new.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_SPHERICAL_POLAR) coordinates_new_scale = fm.createFieldCoordinateTransformation(sp_new) coordinates_new_scale.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) coordinates_new = fm.createFieldDivide(coordinates_new_scale, xyz_scale) fieldassignment = coordinates.createFieldassignment(coordinates_new) result = fieldassignment.setNodeset(baseNodesetGroup) fieldassignment.assign() baseNodesetGroup = None tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) tricubicHermiteBasis = fm.createElementbasis(3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) eft = tricubichermite.createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate.defineField(coordinates, -1, eft) crossAngle = math.pi/8 sinCrossAngle = math.sin(crossAngle) cosCrossAngle = math.cos(crossAngle) edgeRotateCrossAngle = math.pi/24 sinEdgeRotateCrossAngle = math.sin(edgeRotateCrossAngle) cosEdgeRotateCrossAngle = math.cos(edgeRotateCrossAngle) # create RV nodes and modify adjoining LV nodes elementsCountUpRV = elementsCountUp - elementsCountBelowSeptum lv_bni_base = 3 + elementsCountThroughLVWall*now + (elementsCountBelowSeptum - 1)*elementsCountAround nodeIdentifier = startNodeIdentifier = getMaximumNodeIdentifier(nodes) + 1 baseExtraRVWidth = LVWallThickness*(1.0 - LVWallThicknessRatioBase) rv_nids = [] for n3 in range(2): # only 1 element through RV free wall onInside = n3 == 0 for n2 in range(elementsCountUpRV + 1): #if n2 > (elementsCountUpRV - elementsCountUpBase): # theta = math.pi*0.5 #else: # theta = (n2 / (elementsCountUpRV - elementsCountUpBase))*math.pi*0.5 #theta = (n2 / elementsCountUpRV)*math.pi*0.5 RVWidth = RVWidthTop #*math.sin(theta) #RVWidth = RVWidthTop #if n2 == 0: # edgeOffsetInner = RVWidth*0.25 #else: edgeOffsetInner = RVWidth*0.65 if n2 == elementsCountUpRV: RVWidth += baseExtraRVWidth edgeOffsetInner = edgeOffsetInner + baseExtraRVWidth*0.5 onBottomEdge = (n2 == 0) for n1 in range(elementsCountAcrossSeptum + 1): onSideEdge1 = (n1 == 0) onSideEdge2 = (n1 == elementsCountAcrossSeptum) onSideEdge = onSideEdge1 or onSideEdge2 existingNodeIdentifier = lv_bni_base + n2*nor + n1 baseNode = nodes.findNodeByIdentifier(existingNodeIdentifier) cache.setNode(baseNode) result, base_x = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) result, base_dx_ds1 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, 3) result, base_dx_ds2 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, 3) result, base_dx_ds3 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, 3) #print('node ', existingNodeIdentifier, 'dx_ds3', result, base_dx_ds3) mag = math.sqrt(base_dx_ds3[0]*base_dx_ds3[0] + base_dx_ds3[1]*base_dx_ds3[1] + base_dx_ds3[2]*base_dx_ds3[2]) unitOutward = [ base_dx_ds3[0]/mag, base_dx_ds3[1]/mag, base_dx_ds3[2]/mag ] baseRadiusAround = unitOutward[0]*base_x[0] + unitOutward[1]*base_x[1] + unitOutward[2]*base_x[2] rotateEdge = False if onInside: if onBottomEdge and onSideEdge: offset = 0.0 elif onBottomEdge or onSideEdge: offset = edgeOffsetInner rotateEdge = True else: offset = RVWidth else: if onBottomEdge and onSideEdge: offset = RVFreeWallThickness #rotateEdge = True elif onBottomEdge or onSideEdge: offset = edgeOffsetInner # + RVFreeWallThickness rotateEdge = True else: offset = RVWidth + RVFreeWallThickness x = [ base_x[0] + unitOutward[0]*offset, base_x[1] + unitOutward[1]*offset, base_x[2] + unitOutward[2]*offset, ] scale1 = (baseRadiusAround + offset)/baseRadiusAround dx_ds1 = [ base_dx_ds1[0]*scale1, base_dx_ds1[1]*scale1, base_dx_ds1[2]*scale1 ] # GRC not sure if appropriate to use scale1 here: dx_ds2 = [ base_dx_ds2[0]*scale1, base_dx_ds2[1]*scale1, base_dx_ds2[2]*scale1 ] scale3 = RVFreeWallThickness dx_ds3 = [ unitOutward[0]*scale3, unitOutward[1]*scale3, unitOutward[2]*scale3 ] if rotateEdge: if onSideEdge1: rotatedOutward = [ cosEdgeRotateCrossAngle*base_dx_ds3[0] - sinEdgeRotateCrossAngle*base_dx_ds1[0], cosEdgeRotateCrossAngle*base_dx_ds3[1] - sinEdgeRotateCrossAngle*base_dx_ds1[1], cosEdgeRotateCrossAngle*base_dx_ds3[2] - sinEdgeRotateCrossAngle*base_dx_ds1[2] ] elif onSideEdge2: rotatedOutward = [ cosEdgeRotateCrossAngle*base_dx_ds3[0] + sinEdgeRotateCrossAngle*base_dx_ds1[0], cosEdgeRotateCrossAngle*base_dx_ds3[1] + sinEdgeRotateCrossAngle*base_dx_ds1[1], cosEdgeRotateCrossAngle*base_dx_ds3[2] + sinEdgeRotateCrossAngle*base_dx_ds1[2] ] else: # onBottomEdge: rotatedOutward = [ cosEdgeRotateCrossAngle*base_dx_ds3[0] - sinEdgeRotateCrossAngle*base_dx_ds2[0], cosEdgeRotateCrossAngle*base_dx_ds3[1] - sinEdgeRotateCrossAngle*base_dx_ds2[1], cosEdgeRotateCrossAngle*base_dx_ds3[2] - sinEdgeRotateCrossAngle*base_dx_ds2[2] ] mag = math.sqrt(rotatedOutward[0]*rotatedOutward[0] + rotatedOutward[1]*rotatedOutward[1] + rotatedOutward[2]*rotatedOutward[2]) unitRotatedOutward = [ rotatedOutward[0]/mag, rotatedOutward[1]/mag, rotatedOutward[2]/mag ] scale3r = RVFreeWallThickness # RVFreeWallThickness + edgeOffsetInner if not onInside: x[0] += unitRotatedOutward[0]*scale3r x[1] += unitRotatedOutward[1]*scale3r x[2] += unitRotatedOutward[2]*scale3r dx_ds3 = [ unitRotatedOutward[0]*scale3r, unitRotatedOutward[1]*scale3r, unitRotatedOutward[2]*scale3r ] if onBottomEdge: rscale2 = math.sqrt(dx_ds2[0]*dx_ds2[0] + dx_ds2[1]*dx_ds2[1] + dx_ds2[2]*dx_ds2[2]) tang = [ unitRotatedOutward[1]*dx_ds1[2] - unitRotatedOutward[2]*dx_ds1[1], unitRotatedOutward[2]*dx_ds1[0] - unitRotatedOutward[0]*dx_ds1[2], unitRotatedOutward[0]*dx_ds1[1] - unitRotatedOutward[1]*dx_ds1[0] ] rscale2 /= math.sqrt(tang[0]*tang[0] + tang[1]*tang[1] + tang[2]*tang[2]) dx_ds2 = [ tang[0]*rscale2, tang[1]*rscale2, tang[2]*rscale2 ] if onSideEdge: rscale1 = math.sqrt(dx_ds1[0]*dx_ds1[0] + dx_ds1[1]*dx_ds1[1] + dx_ds1[2]*dx_ds1[2]) tang = [ dx_ds2[1]*unitRotatedOutward[2] - dx_ds2[2]*unitRotatedOutward[1], dx_ds2[2]*unitRotatedOutward[0] - dx_ds2[0]*unitRotatedOutward[2], dx_ds2[0]*unitRotatedOutward[1] - dx_ds2[1]*unitRotatedOutward[0] ] rscale1 /= math.sqrt(tang[0]*tang[0] + tang[1]*tang[1] + tang[2]*tang[2]) dx_ds1 = [ tang[0]*rscale1, tang[1]*rscale1, tang[2]*rscale1 ] if onInside and onBottomEdge and onSideEdge: node = baseNode else: node = nodes.createNode(nodeIdentifier, nodetemplate) nodeIdentifier += 1 cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) rv_nids.append(node.getIdentifier()) # create RV elements and modify adjoining LV element fields elementIdentifier = elementsCountThroughLVWall*elementsCountUp*elementsCountAround + 1 scalefactors5 = [ -1.0, sinCrossAngle, cosCrossAngle, sinCrossAngle, cosCrossAngle ] scalefactors9 = [ -1.0, 0.5, 0.25, 0.125, 0.75, sinCrossAngle, cosCrossAngle, sinCrossAngle, cosCrossAngle ] eow = elementsCountUp*elementsCountAround RVSeptumElementIdBase = eow*(elementsCountThroughLVWall - 1) + elementsCountBelowSeptum*elementsCountAround + 2 # Add RV elements # RV LV joiner bottom elements: h-shape eftRVBottom = tricubichermite.createEftBasic() setEftScaleFactorIds(eftRVBottom, [1], []) # d/dxi2 is reversed on inside for ln in [1, 2]: n = ln - 1 mapEftFunction1Node1Term(eftRVBottom, n*8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, [1]) # d/dxi3 = -d/dS2 on LV side for ln in [1, 2, 5, 6]: n = ln - 1 mapEftFunction1Node1Term(eftRVBottom, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [1]) #print('eftRVBottom', eftRVBottom.validate()) # RV LV joiner side 1 elements: h-shape eftRVSide1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eftRVSide1, [1], []) # d/dxi1 is reversed on inside for ln in [1, 3]: n = ln - 1 mapEftFunction1Node1Term(eftRVSide1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) # d/dxi3 = -d/dS1 on LV side for ln in [1, 3, 5, 7]: n = ln - 1 mapEftFunction1Node1Term(eftRVSide1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) #print('eftRVSide1', eftRVSide1.validate()) # RV LV joiner side 2 elements: h-shape eftRVSide2 = tricubichermite.createEftBasic() setEftScaleFactorIds(eftRVSide2, [1], []) # d/dxi1 is reversed on inside for ln in [2, 4]: n = ln - 1 mapEftFunction1Node1Term(eftRVSide2, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) # d/dxi3 = d/dS1 on LV side for ln in [2, 4, 6, 8]: n = ln - 1 mapEftFunction1Node1Term(eftRVSide2, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, []) #print('eftRVSide2', eftRVSide2.validate()) rv_nor = (elementsCountAcrossSeptum + 1) rv_now = (elementsCountUpRV + 1)*rv_nor for n2 in range(-1, elementsCountUpRV): for n1 in range(-1, elementsCountAcrossSeptum + 1): bni = lv_bni_base + n2*elementsCountAround + n1 rv_bni = n2*rv_nor + n1 eft1 = None if n2 == -1: if n1 == -1: # pinched to zero thickness on 3 sides eft1 = tricubichermite.createEftNoCrossDerivatives() remapEftNodeValueLabel(eft1, [ 1, 2, 3, 5, 6, 7 ], Node.VALUE_LABEL_D_DS3, []) ln_map = [ 1, 2, 3, 4, 1, 2, 3, 5 ] remapEftLocalNodes(eft1, 5, ln_map) nodeIdentifiers = [ bni, bni + 1, bni + nor, bni + nor + 1, rv_nids[rv_bni + rv_nor + rv_now + 1] ] #print('eft1 corner a', eft1.validate()) elif n1 == elementsCountAcrossSeptum: # pinched to zero thickness on 3 sides eft1 = tricubichermite.createEftNoCrossDerivatives() remapEftNodeValueLabel(eft1, [ 1, 2, 4, 5, 6, 8 ], Node.VALUE_LABEL_D_DS3, []) ln_map = [ 1, 2, 3, 4, 1, 2, 5, 4 ] remapEftLocalNodes(eft1, 5, ln_map) nodeIdentifiers = [ bni, bni + 1, bni + nor, bni + nor + 1, rv_nids[rv_bni + rv_nor + rv_now] ] #print('eft1 corner b', eft1.validate()) else: nodeIdentifiers = [ bni + nor, bni + nor + 1, rv_nids[rv_bni + rv_nor], rv_nids[rv_bni + rv_nor + 1], bni, bni + 1, rv_nids[rv_bni + rv_nor + rv_now], rv_nids[rv_bni + rv_nor + 1 + rv_now] ] if n1 == 0: eft1 = tricubichermite.createEftBasic() # GRC to fix: uses repeated nodes instead of reducing to 7 #eft1.setNumberOfLocalNodes(7) setEftScaleFactorIds(eft1, [1], []) # d/dxi2 is zero on left side, reversed on inner right side eft1.setFunctionNumberOfTerms(0*8 + 3, 0) eft1.setFunctionNumberOfTerms(2*8 + 3, 0) mapEftFunction1Node1Term(eft1, 1*8 + 3, 2, Node.VALUE_LABEL_D_DS2, 1, [1]) # d/dxi3 = -d/dS2 on LV side for ln in [1, 2, 5, 6]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [1]) elif n1 == (elementsCountAcrossSeptum - 1): eft1 = tricubichermite.createEftBasic() # GRC to fix: uses repeated nodes instead of reducing to 7 #eft1.setNumberOfLocalNodes(7) setEftScaleFactorIds(eft1, [1], []) # d/dxi2 is zero on right side, reversed on inner left side eft1.setFunctionNumberOfTerms(1*8 + 3, 0) eft1.setFunctionNumberOfTerms(3*8 + 3, 0) mapEftFunction1Node1Term(eft1, 0*8 + 3, 1, Node.VALUE_LABEL_D_DS2, 1, [1]) # d/dxi3 = -d/dS2 on LV side for ln in [1, 2, 5, 6]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [1]) else: eft1 = eftRVBottom else: if n1 == -1: nodeIdentifiers = [ bni + 1, rv_nids[rv_bni + 1], bni + nor + 1, rv_nids[rv_bni + rv_nor + 1], bni, rv_nids[rv_bni + 1 + rv_now], bni + nor, rv_nids[rv_bni + rv_nor + 1 + rv_now] ] if n2 == 0: eft1 = tricubichermite.createEftBasic() # GRC to fix: uses repeated nodes instead of reducing to 7 #eft1.setNumberOfLocalNodes(7) setEftScaleFactorIds(eft1, [1], []) # d/dxi1 is zero on bottom side, reversed on inner top side eft1.setFunctionNumberOfTerms(0*8 + 2, 0) eft1.setFunctionNumberOfTerms(1*8 + 2, 0) mapEftFunction1Node1Term(eft1, 2*8 + 2, 3, Node.VALUE_LABEL_D_DS1, 1, [1]) # d/dxi3 = -d/dS1 on LV side for ln in [1, 3, 5, 7]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) #print('eft1 next', eft1.validate()) else: eft1 = eftRVSide1 elif n1 == elementsCountAcrossSeptum: nodeIdentifiers = [ rv_nids[rv_bni], bni, rv_nids[rv_bni + rv_nor], bni + nor, rv_nids[rv_bni + rv_now], bni + 1, rv_nids[rv_bni + rv_nor + rv_now], bni + nor + 1 ] if n2 == 0: eft1 = tricubichermite.createEftBasic() # GRC to fix: uses repeated nodes instead of reducing to 7 #eft1.setNumberOfLocalNodes(7) setEftScaleFactorIds(eft1, [1], []) # d/dxi1 is zero on bottom side, reversed on inner top side eft1.setFunctionNumberOfTerms(0*8 + 2, 0) eft1.setFunctionNumberOfTerms(1*8 + 2, 0) mapEftFunction1Node1Term(eft1, 3*8 + 2, 4, Node.VALUE_LABEL_D_DS1, 1, [1]) # d/dxi3 = d/dS1 on LV side for ln in [2, 4, 6, 8]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, []) #print('eft1 next', eft1.validate()) else: eft1 = eftRVSide2 else: eft1 = eft nodeIdentifiers = [ rv_nids[rv_bni], rv_nids[rv_bni + 1], rv_nids[rv_bni + rv_nor], rv_nids[rv_bni + rv_nor + 1], rv_nids[rv_bni + rv_now], rv_nids[rv_bni + 1 + rv_now], rv_nids[rv_bni + rv_nor + rv_now], rv_nids[rv_bni + rv_nor + 1 + rv_now] ] useElementtemplate = mesh.createElementtemplate() useElementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) result1 = useElementtemplate.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, useElementtemplate) result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) if eft1.getNumberOfLocalScaleFactors() == 1: element.setScaleFactors(eft1, [-1.0]) #print('RV element create', elementIdentifier, result1, result2, nodeIdentifiers) elementIdentifier += 1 fm.endChange()
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: [] empty list of AnnotationGroup """ elementsCount1 = options['Number of elements 1'] elementsCount2 = options['Number of elements 2'] elementsCount3 = options['Number of elements 3'] useCrossDerivatives = options['Use cross derivatives'] fm = region.getFieldmodule() fm.beginChange() coordinates = findOrCreateFieldCoordinates(fm) 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) 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) mesh = fm.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate.defineField(coordinates, -1, eft) cache = fm.createFieldcache() # create nodes nodeIdentifier = 1 x = [0.0, 0.0, 0.0] dx_ds1 = [1.0 / elementsCount1, 0.0, 0.0] dx_ds2 = [0.0, 1.0 / elementsCount2, 0.0] dx_ds3 = [0.0, 0.0, 1.0 / elementsCount3] zero = [0.0, 0.0, 0.0] for n3 in range(elementsCount3 + 1): x[2] = n3 / elementsCount3 for n2 in range(elementsCount2 + 1): x[1] = n2 / elementsCount2 for n1 in range(elementsCount1 + 1): x[0] = n1 / elementsCount1 node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) if useCrossDerivatives: coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters( cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 # create elements elementIdentifier = 1 no2 = (elementsCount1 + 1) no3 = (elementsCount2 + 1) * no2 for e3 in range(elementsCount3): for e2 in range(elementsCount2): for e1 in range(elementsCount1): element = mesh.createElement(elementIdentifier, elementtemplate) bni = e3 * no3 + e2 * no2 + e1 + 1 nodeIdentifiers = [ bni, bni + 1, bni + no2, bni + no2 + 1, bni + no3, bni + no3 + 1, bni + no2 + no3, bni + no2 + no3 + 1 ] result = element.setNodesByIdentifier(eft, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 fm.endChange() return []
def generateBaseMesh(cls, region, options): ''' Generate the base tricubic Hermite mesh. See also generateMesh(). :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: annotationGroups ''' fm = region.getFieldmodule() coordinates = findOrCreateFieldCoordinates(fm) 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) mesh = fm.findMeshByDimension(3) cache = fm.createFieldcache() elementsCount1 = 2 elementsCount2 = 4 elementsCount3 = 4 # Annotation fiducial point 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 = 1 lNodeIds = [] d1 = [0.5, 0.0, 0.0] d2 = [0.0, 0.5, 0.0] d3 = [0.0, 0.0, 1.0] for n3 in range(elementsCount3 + 1): lNodeIds.append([]) for n2 in range(elementsCount2 + 1): lNodeIds[n3].append([]) for n1 in range(elementsCount1 + 1): lNodeIds[n3][n2].append([]) if n3 < elementsCount3: if (n1 == 0) and ((n2 == 0) or (n2 == elementsCount2)): continue else: if (n2 == 0) or (n2 == elementsCount2) or (n1 == 0): continue node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) x = [0.5 * (n1 - 1), 0.5 * (n2 - 1), 1.0 * n3] coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, d3) lNodeIds[n3][n2][n1] = nodeIdentifier nodeIdentifier += 1 # Create elements mesh = fm.findMeshByDimension(3) eftfactory = eftfactory_tricubichermite(mesh, None) eftRegular = eftfactory.createEftBasic() elementtemplateRegular = mesh.createElementtemplate() elementtemplateRegular.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplateRegular.defineField(coordinates, -1, eftRegular) elementtemplateCustom = mesh.createElementtemplate() elementtemplateCustom.setElementShapeType(Element.SHAPE_TYPE_CUBE) lungGroup = AnnotationGroup(region, get_lung_term("lung")) leftLungGroup = AnnotationGroup(region, get_lung_term("left lung")) rightLungGroup = AnnotationGroup(region, get_lung_term("right lung")) annotationGroups = [leftLungGroup, rightLungGroup, lungGroup] lungMeshGroup = lungGroup.getMeshGroup(mesh) leftLungMeshGroup = leftLungGroup.getMeshGroup(mesh) rightLungMeshGroup = rightLungGroup.getMeshGroup(mesh) eft1 = eftfactory.createEftWedgeCollapseXi1Quadrant([1, 5]) eft2 = eftfactory.createEftWedgeCollapseXi1Quadrant([3, 7]) eft3 = eftfactory.createEftWedgeCollapseXi2Quadrant([5, 6]) eft4 = eftfactory.createEftWedgeCollapseXi2Quadrant([7, 8]) eft5 = eftfactory.createEftWedgeCollapseXi1Quadrant([5, 7]) eft6 = eftfactory.createEftTetrahedronCollapseXi1Xi2Quadrant(8, 2) eft7 = eftfactory.createEftTetrahedronCollapseXi1Xi2Quadrant(6, 3) elementIdentifier = 1 for e3 in range(elementsCount3): for e2 in range(elementsCount2): for e1 in range(elementsCount1): eft = eftRegular nodeIdentifiers = [ lNodeIds[e3][e2][e1], lNodeIds[e3][e2][e1 + 1], lNodeIds[e3][e2 + 1][e1], lNodeIds[e3][e2 + 1][e1 + 1], lNodeIds[e3 + 1][e2][e1], lNodeIds[e3 + 1][e2][e1 + 1], lNodeIds[e3 + 1][e2 + 1][e1], lNodeIds[e3 + 1][e2 + 1][e1 + 1] ] scalefactors = None if (e3 < elementsCount3 - 1): if (e2 == 0) and (e1 == 0): # Back wedge elements nodeIdentifiers.pop(4) nodeIdentifiers.pop(0) eft = eft1 scalefactors = [-1.0] elif (e2 == elementsCount2 - 1) and (e1 == 0): # Front wedge elements nodeIdentifiers.pop(6) nodeIdentifiers.pop(2) eft = eft2 else: if (e2 == 0) and (e1 == 1): # Top back wedge elements nodeIdentifiers.pop(5) nodeIdentifiers.pop(4) eft = eft3 elif (e2 == elementsCount2 - 1) and (e1 == 1): # Top front wedge elements nodeIdentifiers.pop(7) nodeIdentifiers.pop(6) eft = eft4 scalefactors = [-1.0] elif (e2 == 1) and (e1 == 0): # Top middle back wedge element nodeIdentifiers.pop(6) nodeIdentifiers.pop(4) eft = eft5 elif (e2 == 2) and (e1 == 0): # Top middle front wedge element nodeIdentifiers.pop(6) nodeIdentifiers.pop(4) eft = eft5 if (e2 == 0) and (e1 == 0): # Top back tetrahedron element nodeIdentifiers.pop(6) nodeIdentifiers.pop(5) nodeIdentifiers.pop(4) nodeIdentifiers.pop(0) eft = eft6 scalefactors = [-1.0] if (e2 == elementsCount2 - 1) and (e1 == 0): # Top front tetrahedron element nodeIdentifiers.pop(7) nodeIdentifiers.pop(6) nodeIdentifiers.pop(4) nodeIdentifiers.pop(2) eft = eft7 scalefactors = [-1.0] if eft is eftRegular: element = mesh.createElement(elementIdentifier, elementtemplateRegular) else: elementtemplateCustom.defineField(coordinates, -1, eft) element = mesh.createElement(elementIdentifier, elementtemplateCustom) element.setNodesByIdentifier(eft, nodeIdentifiers) if scalefactors: element.setScaleFactors(eft, scalefactors) elementIdentifier += 1 leftLungMeshGroup.addElement(element) lungMeshGroup.addElement(element) # Apex annotation point idx = elementsCount1 * elementsCount2 * ( elementsCount3 - 1) + elementsCount1 * (elementsCount2 // 2) element1 = mesh.findElementByIdentifier(idx) markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) nodeIdentifier += 1 cache.setNode(markerPoint) markerName.assignString(cache, 'apex of left lung') markerLocation.assignMeshLocation(cache, element1, [1.0, 1.0, 1.0]) return annotationGroups