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 = fm.createFieldFiniteElement(3) coordinates.setName('coordinates') coordinates.setManaged(True) coordinates.setTypeCoordinate(True) coordinates.setCoordinateSystemType( Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) coordinates.setComponentName(1, 'x') coordinates.setComponentName(2, 'y') coordinates.setComponentName(3, 'z') 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 generateMesh(region, options): """ :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: None """ 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 = fm.createFieldFiniteElement(3) coordinates.setName('coordinates') coordinates.setManaged(True) coordinates.setTypeCoordinate(True) coordinates.setCoordinateSystemType( Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) coordinates.setComponentName(1, 'x') coordinates.setComponentName(2, 'y') coordinates.setComponentName(3, 'z') 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()
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 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'] elementsCountAround = options['Number of elements around'] elementsCountThroughWall = options['Number of elements through wall'] useCrossDerivatives = options['Use cross derivatives'] 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 = fm.createFieldFiniteElement(3) coordinates.setName('coordinates') coordinates.setManaged(True) coordinates.setTypeCoordinate(True) coordinates.setCoordinateSystemType( Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) coordinates.setComponentName(1, 'x') coordinates.setComponentName(2, 'y') coordinates.setComponentName(3, 'z') 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 mesh = fm.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftBasic() tricubicHermiteBasis = fm.createElementbasis( 3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) # Apex1: collapsed on xi2 = 0 eftApex1 = mesh.createElementfieldtemplate(tricubicHermiteBasis) eftApex1.setNumberOfLocalNodes(6) eftApex1.setNumberOfLocalScaleFactors(13) # GRC: allow scale factor identifier for global -1.0 to be prescribed eftApex1.setScaleFactorType( 1, Elementfieldtemplate.SCALE_FACTOR_TYPE_GLOBAL_GENERAL) eftApex1.setScaleFactorIdentifier(1, 1) for layer in range(2): so = layer * 6 + 1 no = layer * 3 fo = layer * 32 for s in range(6): si = so + s + 1 # 3 scale factors per node: cos(theta), sin(theta), arc angle radians sid = (s // 3) * 100 + ( s % 3) + 1 # add 100 for different 'version' eftApex1.setScaleFactorType( si, Elementfieldtemplate.SCALE_FACTOR_TYPE_NODE_GENERAL) eftApex1.setScaleFactorIdentifier(si, sid) # basis node 1 -> local node 1 ln = no + 1 eftApex1.setTermNodeParameter(fo + 1, 1, ln, Node.VALUE_LABEL_VALUE, 1) # 0 terms = zero parameter for d/dxi1 basis eftApex1.setFunctionNumberOfTerms(fo + 2, 0) # 2 terms for d/dxi2 via general linear map: eftApex1.setFunctionNumberOfTerms(fo + 3, 2) eftApex1.setTermNodeParameter(fo + 3, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftApex1.setTermScaling(fo + 3, 1, [so + 1]) eftApex1.setTermNodeParameter(fo + 3, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftApex1.setTermScaling(fo + 3, 2, [so + 2]) # 2 terms for cross derivative 1 2 to correct circular apex: -sin(theta).phi, cos(theta).phi eftApex1.setFunctionNumberOfTerms(fo + 4, 2) eftApex1.setTermNodeParameter(fo + 4, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftApex1.setTermScaling(fo + 4, 1, [so + 2, so + 3]) eftApex1.setTermNodeParameter(fo + 4, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftApex1.setTermScaling(fo + 4, 2, [1, so + 1, so + 3]) eftApex1.setTermNodeParameter(fo + 5, 1, ln, Node.VALUE_LABEL_D_DS3, 1) # 0 terms = zero parameter for cross derivative 1 3, 2 3 and 1 2 3 eftApex1.setFunctionNumberOfTerms(fo + 6, 0) eftApex1.setFunctionNumberOfTerms(fo + 7, 0) eftApex1.setFunctionNumberOfTerms(fo + 8, 0) # basis node 2 -> local node 1 eftApex1.setTermNodeParameter(fo + 9, 1, ln, Node.VALUE_LABEL_VALUE, 1) # 0 terms = zero parameter for d/dxi1 basis eftApex1.setFunctionNumberOfTerms(fo + 10, 0) # 2 terms for d/dxi2 via general linear map: eftApex1.setFunctionNumberOfTerms(fo + 11, 2) eftApex1.setTermNodeParameter(fo + 11, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftApex1.setTermScaling(fo + 11, 1, [so + 4]) eftApex1.setTermNodeParameter(fo + 11, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftApex1.setTermScaling(fo + 11, 2, [so + 5]) # 2 terms for cross derivative 1 2 to correct circular apex: -sin(theta).phi, cos(theta).phi eftApex1.setFunctionNumberOfTerms(fo + 12, 2) eftApex1.setTermNodeParameter(fo + 12, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftApex1.setTermScaling(fo + 12, 1, [so + 5, so + 6]) eftApex1.setTermNodeParameter(fo + 12, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftApex1.setTermScaling(fo + 12, 2, [1, so + 4, so + 6]) eftApex1.setTermNodeParameter(fo + 13, 1, ln, Node.VALUE_LABEL_D_DS3, 1) # 0 terms = zero parameter for cross derivative 1 3, 2 3 and 1 2 3 eftApex1.setFunctionNumberOfTerms(fo + 14, 0) eftApex1.setFunctionNumberOfTerms(fo + 15, 0) eftApex1.setFunctionNumberOfTerms(fo + 16, 0) # basis nodes 3, 4 -> regular local nodes 2, 3 for bn in range(2, 4): fo2 = fo + bn * 8 ni = no + bn eftApex1.setTermNodeParameter(fo2 + 1, 1, ni, Node.VALUE_LABEL_VALUE, 1) eftApex1.setTermNodeParameter(fo2 + 2, 1, ni, Node.VALUE_LABEL_D_DS1, 1) eftApex1.setTermNodeParameter(fo2 + 3, 1, ni, Node.VALUE_LABEL_D_DS2, 1) eftApex1.setTermNodeParameter(fo2 + 5, 1, ni, Node.VALUE_LABEL_D_DS3, 1) if useCrossDerivatives: eftApex1.setTermNodeParameter(fo2 + 4, 1, ni, Node.VALUE_LABEL_D2_DS1DS2, 1) eftApex1.setTermNodeParameter(fo2 + 6, 1, ni, Node.VALUE_LABEL_D2_DS1DS3, 1) eftApex1.setTermNodeParameter(fo2 + 7, 1, ni, Node.VALUE_LABEL_D2_DS2DS3, 1) eftApex1.setTermNodeParameter( fo2 + 8, 1, ni, Node.VALUE_LABEL_D3_DS1DS2DS3, 1) else: eftApex1.setFunctionNumberOfTerms(fo2 + 4, 0) eftApex1.setFunctionNumberOfTerms(fo2 + 6, 0) eftApex1.setFunctionNumberOfTerms(fo2 + 7, 0) eftApex1.setFunctionNumberOfTerms(fo2 + 8, 0) # Apex2: collapsed on xi2 = 1 eftApex2 = mesh.createElementfieldtemplate(tricubicHermiteBasis) eftApex2.setNumberOfLocalNodes(6) eftApex2.setNumberOfLocalScaleFactors(8) eftApex2.setNumberOfLocalScaleFactors(13) # GRC: allow scale factor identifier for global -1.0 to be prescribed eftApex2.setScaleFactorType( 1, Elementfieldtemplate.SCALE_FACTOR_TYPE_GLOBAL_GENERAL) eftApex2.setScaleFactorIdentifier(1, 1) for layer in range(2): so = layer * 6 + 1 no = layer * 3 fo = layer * 32 for s in range(6): si = so + s + 1 # 3 scale factors per node: cos(theta), sin(theta), arc angle radians sid = (s // 3) * 100 + ( s % 3) + 1 # add 100 for different 'version' eftApex2.setScaleFactorType( si, Elementfieldtemplate.SCALE_FACTOR_TYPE_NODE_GENERAL) eftApex2.setScaleFactorIdentifier(si, sid) # basis nodes 1, 2 -> regular local nodes 1, 2 (for each layer) for bn in range(2): fo2 = fo + bn * 8 ni = no + bn + 1 eftApex2.setTermNodeParameter(fo2 + 1, 1, ni, Node.VALUE_LABEL_VALUE, 1) eftApex2.setTermNodeParameter(fo2 + 2, 1, ni, Node.VALUE_LABEL_D_DS1, 1) eftApex2.setTermNodeParameter(fo2 + 3, 1, ni, Node.VALUE_LABEL_D_DS2, 1) eftApex2.setTermNodeParameter(fo2 + 5, 1, ni, Node.VALUE_LABEL_D_DS3, 1) if useCrossDerivatives: eftApex2.setTermNodeParameter(fo2 + 4, 1, ni, Node.VALUE_LABEL_D2_DS1DS2, 1) eftApex2.setTermNodeParameter(fo2 + 6, 1, ni, Node.VALUE_LABEL_D2_DS1DS3, 1) eftApex2.setTermNodeParameter(fo2 + 7, 1, ni, Node.VALUE_LABEL_D2_DS2DS3, 1) eftApex2.setTermNodeParameter( fo2 + 8, 1, ni, Node.VALUE_LABEL_D3_DS1DS2DS3, 1) else: eftApex2.setFunctionNumberOfTerms(fo2 + 4, 0) eftApex2.setFunctionNumberOfTerms(fo2 + 6, 0) eftApex2.setFunctionNumberOfTerms(fo2 + 7, 0) eftApex2.setFunctionNumberOfTerms(fo2 + 8, 0) # basis node 3 -> local node 3 ln = no + 3 fo3 = fo + 16 eftApex2.setTermNodeParameter(fo3 + 1, 1, ln, Node.VALUE_LABEL_VALUE, 1) # 0 terms = zero parameter for d/dxi1 basis eftApex2.setFunctionNumberOfTerms(fo3 + 2, 0) # 2 terms for d/dxi2 via general linear map: eftApex2.setFunctionNumberOfTerms(fo3 + 3, 2) eftApex2.setTermNodeParameter(fo3 + 3, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftApex2.setTermScaling(fo3 + 3, 1, [so + 1]) eftApex2.setTermNodeParameter(fo3 + 3, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftApex2.setTermScaling(fo3 + 3, 2, [so + 2]) # 2 terms for cross derivative 1 2 to correct circular apex: -sin(theta).phi, cos(theta).phi eftApex2.setFunctionNumberOfTerms(fo3 + 4, 2) eftApex2.setTermNodeParameter(fo3 + 4, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftApex2.setTermScaling(fo3 + 4, 1, [1, so + 2, so + 3]) eftApex2.setTermNodeParameter(fo3 + 4, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftApex2.setTermScaling(fo3 + 4, 2, [so + 1, so + 3]) eftApex2.setTermNodeParameter(fo3 + 5, 1, ln, Node.VALUE_LABEL_D_DS3, 1) # 0 terms = zero parameter for cross derivative 1 3, 2 3 and 1 2 3 eftApex2.setFunctionNumberOfTerms(fo3 + 6, 0) eftApex2.setFunctionNumberOfTerms(fo3 + 7, 0) eftApex2.setFunctionNumberOfTerms(fo3 + 8, 0) # basis node 4 -> local node 3 eftApex2.setTermNodeParameter(fo3 + 9, 1, ln, Node.VALUE_LABEL_VALUE, 1) # 0 terms = zero parameter for d/dxi1 basis eftApex2.setFunctionNumberOfTerms(fo3 + 10, 0) # 2 terms for d/dxi2 via general linear map: eftApex2.setFunctionNumberOfTerms(fo3 + 11, 2) eftApex2.setTermNodeParameter(fo3 + 11, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftApex2.setTermScaling(fo3 + 11, 1, [so + 4]) eftApex2.setTermNodeParameter(fo3 + 11, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftApex2.setTermScaling(fo3 + 11, 2, [so + 5]) # 2 terms for cross derivative 1 2 to correct circular apex: -sin(theta).phi, cos(theta).phi eftApex2.setFunctionNumberOfTerms(fo3 + 12, 2) eftApex2.setTermNodeParameter(fo3 + 12, 1, ln, Node.VALUE_LABEL_D_DS1, 1) eftApex2.setTermScaling(fo3 + 12, 1, [1, so + 5, so + 6]) eftApex2.setTermNodeParameter(fo3 + 12, 2, ln, Node.VALUE_LABEL_D_DS2, 1) eftApex2.setTermScaling(fo3 + 12, 2, [so + 4, so + 6]) eftApex2.setTermNodeParameter(fo3 + 13, 1, ln, Node.VALUE_LABEL_D_DS3, 1) # 0 terms = zero parameter for cross derivative 1 3, 2 3 and 1 2 3 eftApex2.setFunctionNumberOfTerms(fo3 + 14, 0) eftApex2.setFunctionNumberOfTerms(fo3 + 15, 0) eftApex2.setFunctionNumberOfTerms(fo3 + 16, 0) elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate.defineField(coordinates, -1, eft) elementtemplateApex1 = mesh.createElementtemplate() elementtemplateApex1.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplateApex1.defineField(coordinates, -1, eftApex1) elementtemplateApex2 = mesh.createElementtemplate() elementtemplateApex2.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplateApex2.defineField(coordinates, -1, eftApex2) 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 apex1 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]) 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) 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 else: # create apex2 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]) 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 Apex1 elements, editing eft scale factor identifiers around apex # scale factor identifiers follow convention of offsetting by 100 for each 'version' for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eftApex1.setScaleFactorIdentifier(2, va * 100 + 1) eftApex1.setScaleFactorIdentifier(3, va * 100 + 2) eftApex1.setScaleFactorIdentifier(4, va * 100 + 3) eftApex1.setScaleFactorIdentifier(5, vb * 100 + 1) eftApex1.setScaleFactorIdentifier(6, vb * 100 + 2) eftApex1.setScaleFactorIdentifier(7, vb * 100 + 3) eftApex1.setScaleFactorIdentifier(8, va * 100 + 1) eftApex1.setScaleFactorIdentifier(9, va * 100 + 2) eftApex1.setScaleFactorIdentifier(10, va * 100 + 3) eftApex1.setScaleFactorIdentifier(11, vb * 100 + 1) eftApex1.setScaleFactorIdentifier(12, vb * 100 + 2) eftApex1.setScaleFactorIdentifier(13, vb * 100 + 3) # redefine field in template for changes to eftApex1: elementtemplateApex1.defineField(coordinates, -1, eftApex1) element = mesh.createElement(elementIdentifier, elementtemplateApex1) 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(eftApex1, 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(eftApex1, 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 Apex2 elements, editing eft scale factor identifiers around apex # scale factor identifiers follow convention of offsetting by 100 for each 'version' for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eftApex2.setScaleFactorIdentifier(2, va * 100 + 1) eftApex2.setScaleFactorIdentifier(3, va * 100 + 2) eftApex2.setScaleFactorIdentifier(4, va * 100 + 3) eftApex2.setScaleFactorIdentifier(5, vb * 100 + 1) eftApex2.setScaleFactorIdentifier(6, vb * 100 + 2) eftApex2.setScaleFactorIdentifier(7, vb * 100 + 3) eftApex2.setScaleFactorIdentifier(8, va * 100 + 1) eftApex2.setScaleFactorIdentifier(9, va * 100 + 2) eftApex2.setScaleFactorIdentifier(10, va * 100 + 3) eftApex2.setScaleFactorIdentifier(11, vb * 100 + 1) eftApex2.setScaleFactorIdentifier(12, vb * 100 + 2) eftApex2.setScaleFactorIdentifier(13, vb * 100 + 3) # redefine field in template for changes to eftApex2: elementtemplateApex1.defineField(coordinates, -1, eftApex2) element = mesh.createElement(elementIdentifier, elementtemplateApex1) 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(eftApex2, 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(eftApex2, 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()
def generateMesh(region, options): """ :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: None """ elementsCountAlong = options['Number of elements along'] elementsCountAround = options['Number of elements around'] elementsCountThroughWall = options['Number of elements through wall'] wallThickness = options['Wall thickness'] useCrossDerivatives = options['Use cross derivatives'] fm = region.getFieldmodule() fm.beginChange() coordinates = fm.createFieldFiniteElement(3) coordinates.setName('coordinates') coordinates.setManaged(True) coordinates.setTypeCoordinate(True) coordinates.setCoordinateSystemType( Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) coordinates.setComponentName(1, 'x') coordinates.setComponentName(2, 'y') coordinates.setComponentName(3, 'z') 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 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'] useCrossDerivatives = options['Use cross derivatives'] fm = region.getFieldmodule() fm.beginChange() coordinates = fm.createFieldFiniteElement(3) coordinates.setName('coordinates') coordinates.setManaged(True) coordinates.setTypeCoordinate(True) coordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) coordinates.setComponentName(1, 'x') coordinates.setComponentName(2, 'y') coordinates.setComponentName(3, 'z') 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()
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'] lvWallThicknessRatioBase = options['LV wall thickness ratio base'] 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 MeshType_3d_heartventricles1.generateMesh(region, options) 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 lvDisplacementRadians = math.pi*2.0/3.0 coslvDisplacementRadians = math.cos(lvDisplacementRadians) sinlvDisplacementRadians = math.sin(lvDisplacementRadians) outletJoinRadians = math.pi # *0.8 cosOutletJoinRadians = math.cos(outletJoinRadians) sinOutletJoinRadians = math.sin(outletJoinRadians) lvOutletOffset = 0.5 - lvWallThickness*(1.0 - lvWallThicknessRatioBase) - lvOutletRadius - lvOutletWallThickness #lvOutletCentreX = cosOutletJoinRadians*lvOutletOffset #lvOutletCentreY = sinOutletJoinRadians*lvOutletOffset lvOutletCentreX = lvOutletOffset*math.cos(lvDisplacementRadians) lvOutletCentreY = lvOutletOffset*math.sin(lvDisplacementRadians) # 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 ] outletScale3 = 0.15 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) or (n1 == 3)) 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] = outletScale3*cosRadiansAround dx_ds3[1] = outletScale3*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 nodes on top and bottom of supraventricular crest centre where it meets # between nodes 141 and 130 for i in range(2): radiansAround = outletJoinRadians - math.pi + (1.5 + 0.5*i)*radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) scale1 = 0.15 distance = 0.15 if i == 0: scale2 = 0.25 else: scale2 = 0.1 x = [ lvOutletCentreX + cosRadiansAround*(lvOutletRadius + lvOutletWallThickness + distance), lvOutletCentreY + sinRadiansAround*(lvOutletRadius + lvOutletWallThickness + distance), baseHeight ] dx_ds1 = [ -sinRadiansAround*scale1, cosRadiansAround*scale1, 0.0 ] dx_ds2 = [ -cosRadiansAround*scale2, -sinRadiansAround*scale2, 0.0 ] dx_ds3 = [ 0.0, 0.0, baseThickness ] node = nodes.createNode(nodeIdentifier, nodetemplateFull) 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 i == 1: crest_nid1 = nodeIdentifier else: ra_nid1 = nodeIdentifier nodeIdentifier += 1 x[2] = baseHeight + baseThickness node = nodes.createNode(nodeIdentifier, nodetemplateFull) 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) if i == 1: crest_nid2 = nodeIdentifier else: ra_nid2 = nodeIdentifier nodeIdentifier += 1 # place nodes below RV outlet nodes 143, 144 for nid in [143, 144]: node1 = nodes.findNodeByIdentifier(nid) cache.setNode(node1) result, x = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) result, dx_ds1 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, 3) dx_ds2 = [ 0.0, 0.0, baseThickness ] result, dx_ds3 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, 3) for c in range(3): dx_ds3[c] *= lvOutletWallThickness/outletScale3 x[2] -= baseThickness node = nodes.createNode(nodeIdentifier, nodetemplateFull) nodeIdentifier += 1 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) if False: # create extra nodes on LV 'crest' and edge of RA for i in range(2): scale1 = 0.08 scale2 = 0.2 x = [ -0.06 - scale1*i, -0.16, baseHeight ] dx_ds1 = [ scale1, 0.0, 0.0 ] dx_ds2 = [ 0.0, scale2, 0.0 ] dx_ds3 = [ 0.0, 0.0, baseThickness ] node = nodes.createNode(nodeIdentifier, nodetemplateFull) 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) nodeIdentifier += 1 x[2] = baseHeight + baseThickness node = nodes.createNode(nodeIdentifier, nodetemplateFull) 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 # create elements elementIdentifier = startElementIdentifier = getMaximumElementIdentifier(mesh) + 1 # crux of the heart - collapsed on 1 side, and partially on the other eft1 = tricubichermite.createEftBasic() ln_map = [ 1, 2, 1, 3, 4, 5, 1, 6 ] remapEftLocalNodes(eft1, 6, ln_map) setEftScaleFactorIds(eft1, [1], []) for n in [0, 2, 4]: ln = ln_map[n] eft1.setFunctionNumberOfTerms(n*8 + 3, 0) for n in [2]: ln = ln_map[n] eft1.setFunctionNumberOfTerms(n*8 + 5, 0) for n in [4, 6]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) for n in [2, 6]: ln = ln_map[n] eft1.setFunctionNumberOfTerms(n*8 + 5, 0) tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 3, 6, 1) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 39, 40, 133, 88, 89, 139 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element crux', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # Mitral - Aorta separator, collapsed on one end and one side eft1 = tricubichermite.createEftBasic() ln_map = [ 1, 2, 3, 4, 1, 2, 5, 6 ] remapEftLocalNodes(eft1, 6, ln_map) setEftScaleFactorIds(eft1, [1], []) for n in [3, 7]: ln = ln_map[n] #eft1.setFunctionNumberOfTerms(n*8 + 3, 0) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, []) for n in [0, 1, 4, 5]: ln = ln_map[n] eft1.setFunctionNumberOfTerms(n*8 + 5, 0) for n in [1, 5]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, []) for n in [0]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 5, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 6, 1) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 161, 39, 138, 133, 144, 139 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element Mitral - Aorta separator', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 eftLinearOutlet = tricubichermite.createEftBasic() setEftScaleFactorIds(eftLinearOutlet, [1], []) # transition to no derivatives at outlet tricubichermite.setEftLinearDerivativeXi3(eftLinearOutlet, 3, 7, 3, 7, 1) tricubichermite.setEftLinearDerivativeXi3(eftLinearOutlet, 4, 8, 4, 8, 1) elementtemplateLinearOutlet = mesh.createElementtemplate() elementtemplateLinearOutlet.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplateLinearOutlet.defineField(coordinates, -1, eftLinearOutlet) if False: # triangle wedges on top of ventricular septum # collapsed at xi2 = 1, angling in xi3 = 0 face eftSeptumWedge = tricubichermite.createEftBasic() eftSeptumWedge.setNumberOfLocalNodes(6) setEftScaleFactorIds(eftSeptumWedge, [1], []) for ln in [5, 6]: n = ln - 1 # general map d/ds2 to get angle: mapEftFunction1Node2Terms(eftSeptumWedge, n*8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, [], Node.VALUE_LABEL_D_DS3, 1, [1]) for ln in [3, 4]: n = ln + 3 # shape value and d/ds1 eftSeptumWedge.setTermNodeParameter(n*8 + 1, 1, ln, Node.VALUE_LABEL_VALUE, 1) eftSeptumWedge.setTermNodeParameter(n*8 + 2, 1, ln, Node.VALUE_LABEL_D_DS1, 1) # general map d/ds2 to get angle: mapEftFunction1Node2Terms(eftSeptumWedge, n*8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, [], Node.VALUE_LABEL_D_DS3, 1, [1]) # zero d/dxi3 eftSeptumWedge.setFunctionNumberOfTerms(n*8 + 5, 0) print('eftSeptumWedge.validate()', eftSeptumWedge.validate()) elementtemplateSeptumWedge = mesh.createElementtemplate() elementtemplateSeptumWedge.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplateSeptumWedge.defineField(coordinates, -1, eftSeptumWedge) septumNids = [ [ 40, 41, 133, 134, 89, 90, 139, 140 ], [ 41, 42, 134, 135, 90, 91, 140, 141 ], [ 42, 43, 135, 136, 91, 92, 141, 142 ], [ 43, 44, 136, 137, 92, 93, 142, 143 ] ] for i in range(4): element = mesh.createElement(elementIdentifier, elementtemplateLinearOutlet) result2 = element.setNodesByIdentifier(eftLinearOutlet, septumNids[i]) result3 = element.setScaleFactors(eftLinearOutlet, [-1]) print('create septum element', elementIdentifier, result2, result3, septumNids[i]) elementIdentifier += 1 # supraventricular crest at RA by RV free wall eft1 = tricubichermite.createEftBasic() eft1.setTermNodeParameter(0*8 + 3, 1, 1, Node.VALUE_LABEL_D_DS1, 1) eft1.setTermNodeParameter(4*8 + 3, 1, 5, Node.VALUE_LABEL_D_DS1, 1) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 111, 112, 156, 158, 129, 130, 157, 159 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) print('create element supra RA 1', elementIdentifier, result2, nodeIdentifiers1 ) elementIdentifier += 1 # supraventricular crest at PO by RV free wall eft1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eft1, [1], []) tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1) for ln in [4,8]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, []) #mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 112, 113, 158, 147, 130, 131, 159, 152 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element supra PO 1', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # supraventricular crest at RA by RV septum eft1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eft1, [1], []) for ln in [3, 4]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for ln in [7]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for ln in [8]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 156, 158, 90, 91, 157, 159, 140, 141 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element supra RA 2', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # supraventricular crest at PO by RV septum eft1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eft1, [1], []) tricubichermite.setEftLinearDerivativeXi3(eft1, 2, 6, 2, 6, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1) for ln in [3]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for ln in [7]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for ln in [2,6]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) for ln in [4,8]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 158, 147, 91, 146, 159, 152, 141, 151 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element supra PO 2', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # supraventricular crest at PO-AO joiner # This element has some penetraton when very thin. Possible adding a rake angle to septal elements will help eft1 = tricubichermite.createEftBasic() eft1.setNumberOfLocalNodes(7) setEftScaleFactorIds(eft1, [1], []) tricubichermite.setEftLinearDerivativeXi3(eft1, 2, 6, 2, 6, 1) #tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 7, 4, 7, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 7, 1) for ln in [1]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) #print('eft1.validate() 1', eft1.validate()) for ln in [3]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) #eft1.setFunctionNumberOfTerms(n*8 + 5, 0) #print('eft1.validate() 2', eft1.validate()) for ln in [2, 4, 6]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) for ln in [5]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) #print('eft1.validate() 3', eft1.validate()) for n in [6, 7]: ln = 7 # collapse on one edge mapEftFunction1Node1Term(eft1, n*8 + 1, ln, Node.VALUE_LABEL_VALUE, 1, []) eft1.setFunctionNumberOfTerms(n*8 + 2, 0) #eft1.setFunctionNumberOfTerms(n*8 + 3, 0) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, []) #eft1.setFunctionNumberOfTerms(n*8 + 5, 0) for n in [6]: ln = 7 # collapse on one edge mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) #eft1.setFunctionNumberOfTerms(n*8 + 5, 0) print('eft1.validate() 4', eft1.validate()) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 91, 146, 92, 145, 141, 151, 142 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element supra PO AO 3', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 if False: eft1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eft1, [1], []) mapEftFunction1Node1Term(eft1, 0*8 + 2, 1, Node.VALUE_LABEL_D_DS2, 1, [1]) mapEftFunction1Node1Term(eft1, 0*8 + 3, 1, Node.VALUE_LABEL_D_DS1, 1, []) mapEftFunction1Node1Term(eft1, 1*8 + 2, 2, Node.VALUE_LABEL_D_DS2, 1, [1]) mapEftFunction1Node1Term(eft1, 4*8 + 2, 5, Node.VALUE_LABEL_D_DS2, 1, [1]) mapEftFunction1Node1Term(eft1, 4*8 + 3, 5, Node.VALUE_LABEL_D_DS1, 1, []) mapEftFunction1Node1Term(eft1, 5*8 + 2, 6, Node.VALUE_LABEL_D_DS2, 1, [1]) # transition to no derivatives at outlet tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 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) nodeIdentifiers1 = [ 156, 112, 146, 147, 157, 130, 151, 152 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 rvNids = [ [147, 148, 152, 153 ], [148, 149, 153, 154 ] ] i = 0 for eid in [ 67, 68 ]: origElement = mesh.findElementByIdentifier(eid) origEft = origElement.getElementfieldtemplate(coordinates, -1) origNodeIdentifiers = getElementNodeIdentifiers(origElement, origEft) eft1 = origEft setEftScaleFactorIds(eft1, [1], []) # transition to no derivatives at outlet tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 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] 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, [-1.0]) print('create element', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 i += 1 rvNids = [ [147, 148, 152, 153 ], [148, 149, 153, 154 ] ] scalefactors5hanging = [ -1.0, 0.5, 0.25, 0.125, 0.75 ] i = -1 for eid in [ 68 ]: i += 1 origElement = mesh.findElementByIdentifier(eid) origEft = origElement.getElementfieldtemplate(coordinates, -1) origNodeIdentifiers = getElementNodeIdentifiers(origElement, origEft) eft1 = tricubichermite.createEftBasic() 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 # PO segment on RV free wall h element eft1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eft1, [1], []) for ln in [2]: n = ln - 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, []) for ln in [ 6]: n = ln - 1 #mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, []) tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 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) nodeIdentifiers1 = [ 114, 93, 149, 150, 132, 94, 154, 155 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element PO 5', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # Last PO segment on LV/AO side eft1 = tricubichermite.createEftBasic() ln_map = [ 1, 2, 3, 4, 5, 6, 7, 6 ] remapEftLocalNodes(eft1, 7, ln_map) setEftScaleFactorIds(eft1, [1], []) for n in [0, 1, 4, 5]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for n in [0, 1, 4, 5, 7]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) for n in [4]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) for n in [5, 7]: ln = ln_map[n] eft1.setFunctionNumberOfTerms(n*8 + 3, 0) for n in [5]: ln = ln_map[n] eft1.setFunctionNumberOfTerms(n*8 + 5, 0) tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 6, 1) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 93, 92, 150, 145, 143, 142, 155 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element PO 6', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # LV/RV junction 1: element collapsed on 3 edges eft1 = tricubichermite.createEftBasic() ln_map = [ 1, 1, 2, 2, 3, 4, 5, 5 ] remapEftLocalNodes(eft1, 5, ln_map) setEftScaleFactorIds(eft1, [1, 2], []) for n in [0, 1, 2, 3, 6, 7]: ln = ln_map[n] eft1.setFunctionNumberOfTerms(n*8 + 2, 0) for n in [0]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, []) for n in [1]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for n in [4]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, []) #mapEftFunction1Node2Terms(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [], Node.VALUE_LABEL_D_DS2, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, []) # cross derivative to reduce bulge: mapEftFunction1Node2Terms(eft1, n*8 + 4, ln, Node.VALUE_LABEL_D_DS1, 1, [2], Node.VALUE_LABEL_D_DS2, 1, [1, 2]) for n in [5]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 2, 5, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 2, 5, 1) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 93, 150, 94, 143, 155 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0, 4.0]) print('create element LV/RV junction 1', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # LV/RV junction 2: element collapsed on 1 edge # This needs work - non positive jacobian eft1 = tricubichermite.createEftBasic() ln_map = [ 1, 2, 3, 4, 5, 6, 7, 7 ] remapEftLocalNodes(eft1, 7, ln_map) setEftScaleFactorIds(eft1, [1], []) for n in [2]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [1]) for n in [3]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, []) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for n in [6, 7]: ln = ln_map[n] eft1.setFunctionNumberOfTerms(n*8 + 2, 0) for n in [6]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for n in [7]: ln = ln_map[n] mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 44, 45, 137, 160, 93, 94, 143 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element LV/RV junction 2', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # AO 5 eft1 = tricubichermite.createEftBasic() ln_map = [ 1, 2, 3, 4, 1, 2, 5, 6 ] remapEftLocalNodes(eft1, 6, ln_map) setEftScaleFactorIds(eft1, [1], []) for n in [0, 1]: ln = ln_map[n] eft1.setFunctionNumberOfTerms(n*8 + 5, 0) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 5, 1) tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 6, 1) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 160, 161, 137, 138, 143, 144 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element LV/RV junction 2', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 if False: # LV supraventricular crest at LA by LV free wall eft1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eft1, [1], []) for n in [1, 5]: ln = n + 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 46, 47, 164, 162, 95, 96, 165, 163 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element supra LA 1', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # LV supraventricular crest at LA by LV septum 2 eft1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eft1, [1], []) for n in [2, 3, 6, 7]: ln = n + 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for n in [2, 6]: ln = n + 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) for n in [3, 7]: ln = n + 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, []) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 164, 162, 160, 161, 165, 163, 143, 144 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element supra LA 2', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # LV supraventricular crest at LA by LV septum 3 eft1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eft1, [1], []) for n in [2, 6]: ln = n + 1 #mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for n in [3, 7]: ln = n + 1 mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [1]) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 45, 46, 160, 164, 94, 95, 143, 165 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element supra LA 3', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 # LV free wall to AO eft1 = tricubichermite.createEftBasic() setEftScaleFactorIds(eft1, [1], []) for n in [1, 5]: ln = n + 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1]) for n in [2, 3, 6, 7]: ln = n + 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1]) mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, []) for n in [3, 7]: ln = n + 1 mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, []) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) nodeIdentifiers1 = [ 45, 46, 160, 161, 94, 95, 143, 144 ] result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1) result3 = element.setScaleFactors(eft1, [-1.0]) print('create element LV free wall to AO', elementIdentifier, result2, result3, nodeIdentifiers1 ) elementIdentifier += 1 fm.endChange()
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'] elementsCountAround = options['Number of elements around'] elementsCountAcrossSeptum = options['Number of elements across septum'] elementsCountBelowSeptum = options['Number of elements below septum'] elementsCountThroughLVWall = options[ 'Number of elements through LV wall'] 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.generateMesh(region, sphereShellOptions) 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) 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 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]) 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) or (n1 == elementsCountAcrossSeptum): # pinched to zero thickness on 3 sides eft1 = tricubichermite.createEftBasic() eft1.setNumberOfLocalNodes(5) if n1 == -1: unpinched_n1 = 3 nodeIdentifiers = [ bni, bni + 1, bni + nor, bni + nor + 1, rv_nids[rv_bni + rv_nor + rv_now + 1] ] else: unpinched_n1 = 2 nodeIdentifiers = [ bni, bni + 1, bni + nor, bni + nor + 1, rv_nids[rv_bni + rv_nor + rv_now] ] unpinched_n2 = unpinched_n1 + 4 for n in [1, 2, 3, 4]: if n != unpinched_n1: eft1.setFunctionNumberOfTerms(n * 8 + 5, 0) # zero d/dxi3 for n in [4, 5, 6, 7]: ln = 5 if (n == unpinched_n2) else n - 3 mapEftFunction1Node1Term(eft1, n * 8 + 1, ln, Node.VALUE_LABEL_VALUE, 1, []) mapEftFunction1Node1Term(eft1, n * 8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, []) mapEftFunction1Node1Term(eft1, n * 8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, []) if n == unpinched_n2: mapEftFunction1Node1Term( eft1, n * 8 + 5, ln, Node.VALUE_LABEL_D_DS3, 1, []) else: eft1.setFunctionNumberOfTerms(n * 8 + 5, 0) # zero d/dxi3 #print('eft1 corner', 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()