Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Texture coordinates #46

Merged
merged 11 commits into from
Mar 22, 2019
19 changes: 19 additions & 0 deletions scaffoldmaker/utils/eftfactory_bicubichermitelinear.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,22 @@ def createEftSplitXi1RightOut(self):
remapEftNodeValueLabel(eft, [ 5, 7 ], self._d_ds2, [ (self._d_ds1, [1]), (self._d_ds2, []) ])
assert eft.validate(), 'eftfactory_bicubichermitelinear.createEftSplitXi1RightOut: Failed to validate eft'
return eft

def createEftOpenTube(self):
'''
Create a basic bicubic hermite linear element template for elements
along boundary where a tube is opened for a flat preparation. Retain node
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document: opened on xi1 = 1. Could eventually have 6 variants.

numbering with two versions for boundary nodes.
:return: Element field template
'''
eft = self.createEftBasic()
for n in [ 1, 3, 5, 7 ]:
ln = n + 1
eft.setTermNodeParameter(n*4 + 1, 1, ln, Node.VALUE_LABEL_VALUE, 2)
eft.setTermNodeParameter(n*4 + 2, 1, ln, Node.VALUE_LABEL_D_DS1, 2)
eft.setTermNodeParameter(n*4 + 3, 1, ln, Node.VALUE_LABEL_D_DS2, 2)
if self._useCrossDerivatives:
eft.setTermNodeParameter(n*4 + 4, 1, ln, Node.VALUE_LABEL_D2_DS1DS2, 2)

assert eft.validate(), 'eftfactory_bicubichermitelinear.createEftFlattenTube: Failed to validate eft'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function name difference in assert.

return eft
101 changes: 101 additions & 0 deletions scaffoldmaker/utils/tubemesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,107 @@ def generatetubemesh(region,
result = element.setNodesByIdentifier(eft, nodeIdentifiers)
elementIdentifier = elementIdentifier + 1

# Create texture coordinates
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define texture coordinates field

textureCoordinates = getOrCreateTextureCoordinateField(fm)
textureNodetemplate1 = nodes.createNodetemplate()
textureNodetemplate1.defineField(textureCoordinates)
textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_VALUE, 1)
textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D_DS1, 1)
textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D_DS2, 1)
if useCrossDerivatives:
textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1)

textureNodetemplate2 = nodes.createNodetemplate()
textureNodetemplate2.defineField(textureCoordinates)
textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_VALUE, 2)
textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D_DS1, 2)
textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D_DS2, 2)
if useCrossDerivatives:
textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 2)

bicubichermitelinear = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives)
eftTexture = bicubichermitelinear.createEftBasic()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eftTexture1 for consistency with elementtemplate1 below.


elementtemplate1 = mesh.createElementtemplate()
elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
elementtemplate1.defineField(textureCoordinates, -1, eftTexture)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eftTexture1


eftTexture2 = bicubichermitelinear.createEftOpenTube()
elementtemplate2 = mesh.createElementtemplate()
elementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE)
elementtemplate2.defineField(textureCoordinates, -1, eftTexture2)

# Calculate texture coordinates and derivatives
uTexture = []
d1Texture = []
d2Texture = []
for n3 in range(elementsCountThroughWall + 1):
for n2 in range(elementsCountAlong + 1):
for n1 in range(elementsCountAround + 1):
u = [ 1.0 / elementsCountAround * n1,
1.0 / elementsCountAlong * n2,
1.0 / elementsCountThroughWall * n3]
d1 = [1.0 / elementsCountAround, 0.0, 0.0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

d1 and d2 are not functions of n1, n2 or n3, so no need to calculate and store.
Eventually we may have variability around, but I expect we'll be consistent all the way along, or within each segment.
Personally I'd just store the variation with n1 and n3, then define the rest in the node loop.
Only cache coordinates if there is a performance gain from shared calculation.

d2 = [0.0, 1.0 / elementsCountAlong, 0.0]
uTexture.append(u)
d1Texture.append(d1)
d2Texture.append(d2)

nodeIdentifier = nextNodeIdentifier
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change 'next' to 'first' in nextNodeIdentifier, nextElementIdentifier throughout this function as it's clearer.

for n in range(len(uTexture)):
if n%(elementsCountAround+1) == 0.0:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

== 0, not a floating point.
Also, I like spaces around operators % and +.
That said, I would loop as follows:

for n3 in range(elementsCountThroughWall + 1):
    for n2 in range(elementsCountAlong + 1):
        for n1 in range(elementsCountAround):

... and then compute coordinates from those precalculated around one slice.

node = nodes.findNodeByIdentifier(nodeIdentifier)
node.merge(textureNodetemplate2)
cache.setNode(node)
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, uTexture[n])
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1Texture[n])
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2Texture[n])
endIdx = n + elementsCountAround
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, uTexture[endIdx])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the code in the if and else clauses is the same except setting the second version for the last nodes. Put the common code in one place. i.e. at the end:

if n == 0:
    textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, uTexture[endIdx])
    textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, d1Texture[endIdx])
    textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, d2Texture[endIdx])
    if useCrossDerivatives:
        textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero)

textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, d1Texture[endIdx])
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, d2Texture[endIdx])
if useCrossDerivatives:
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero)
nodeIdentifier = nodeIdentifier + 1
elif (n+1)%(elementsCountAround+1) > 0:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to just 'else'.

node = nodes.findNodeByIdentifier(nodeIdentifier)
node.merge(textureNodetemplate1)
cache.setNode(node)
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, uTexture[n])
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1Texture[n])
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2Texture[n])
if useCrossDerivatives:
textureCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
nodeIdentifier = nodeIdentifier + 1

# create texture coordinate elements
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change comment to 'define texture coordinates field over elements
(no new elements are being created)

elementIdentifier = nextElementIdentifier
now = (elementsCountAlong + 1)*elementsCountAround
for e3 in range(elementsCountThroughWall):
for e2 in range(elementsCountAlong):
for e1 in range(elementsCountAround):
if e1 < elementsCountAround - 1:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of this code is common. Change to:

element = mesh.findElementByIdentifier(elementIdentifier)
element.merge(elementtemplate2 if onOpening else elementtemplate1)
... 5 lines bni11 ... nodeIdentifiers
element.setNodesByIdentifier(eftTexture2 if onOpening else eftTexture1, nodeidentifiers)

(note renamed to eftTexture1 for consistency earlier)

element = mesh.findElementByIdentifier(elementIdentifier)
element.merge(elementtemplate1)
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(eftTexture, nodeIdentifiers)
else:
element = mesh.findElementByIdentifier(elementIdentifier)
element.merge(elementtemplate2)
# element = mesh.createElement(elementIdentifier, elementtemplate2)
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]
result2 = element.setNodesByIdentifier(eftTexture2, nodeIdentifiers)
elementIdentifier = elementIdentifier + 1

fm.endChange()

return annotationGroups, nodeIdentifier, elementIdentifier
Expand Down
28 changes: 28 additions & 0 deletions scaffoldmaker/utils/zinc_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,34 @@ def getOrCreateCoordinateField(fieldmodule, name='coordinates', componentsCount=
fieldmodule.endChange()
return coordinates

def getOrCreateTextureCoordinateField(fieldmodule, name='textureCoordinates', componentsCount=3):
'''
Finds or creates a rectangular cartesian texture coordinate field.
New field has component names, 'u', 'v', 'w'.
Raises exception if existing field of name is not finite element type or has incorrect attributes.
:param fieldmodule: Zinc fieldmodule to find or create field in.
:param name: Name of field to find or create.
:param componentsCount: Number of components / dimension of field.
'''
assert (componentsCount > 0) and (componentsCount <= 3), 'getOrCreateCoordinateField. Dimensions must be from 1 to 3'
coordinates = fieldmodule.findFieldByName(name)
if coordinates.isValid():
coordinates = coordinates.castFiniteElement()
assert coordinates.isValid(), 'getOrCreateCoordinateField. Existing field \'' + name + '\' is not finite element type'
assert coordinates.getNumberOfComponents() == componentsCount, 'getOrCreateCoordinateField. Existing field \'' + name + '\' does not have ' + str(componentsCount) + ' components'
assert coordinates.getCoordinateSystemType() == Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN, 'getOrCreateCoordinateField. Existing field \'' + name + '\' is not rectangular Cartesian'
return coordinates
fieldmodule.beginChange()
coordinates = fieldmodule.createFieldFiniteElement(componentsCount)
coordinates.setName(name)
coordinates.setManaged(True)
coordinates.setTypeCoordinate(True)
coordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)
for c in range(componentsCount):
coordinates.setComponentName(c + 1, ['u', 'v', 'w'][c])
fieldmodule.endChange()
return coordinates

def getOrCreateElementXiField(fieldmodule, name='element_xi', mesh=None):
'''
Finds or creates a stored mesh location field for storing locations in the
Expand Down