Hi, I am attaching a function that computes surface strain correctly for triangulated surfaces. Iām not sure whether the VTK version is wrong or just makes different assumptions, but my version, taken from this paper, considers each triangle to be a shell element, and determines the Green-Lagrange strain in the global coordinate system assuming 1) no shear strain of the shell element in the normal plane, and 2) no normal strain in the normal plane.
def compute_GL_strain(surf):
'''
Calculates the Green-Lagrange strain of a surface mesh, assuming it has an array
called "Displacements" at each node. The surface mesh should be in the reference
configuration.
Args:
surf: A PyVista polydata surface in the reference configuration
Returns:
surf: A PyVista polydata surface with an added cell vector array containing the
components of the Green-Lagrange strain tensor.
These calculations are identical to Razeghi 2021 (https://www-nature-com.stanford.idm.oclc.org/articles/s41598-021-84935-x)
Supplementary. The equations are a specific case on shell kinematic theory,
assuming 1) no out-of-plane shear strain, and 2) no normal strain in normal direction.
See surf_def_grad_derivation.pdf for details.
The components of the resulting VTK array correspond to
GreenLagrangeStrain[0] = E_xx
GreenLagrangeStrain[1] = E_xy
GreenLagrangeStrain[2] = E_xz
GreenLagrangeStrain[3] = E_yx
GreenLagrangeStrain[4] = E_yy
GreenLagrangeStrain[5] = E_yz
GreenLagrangeStrain[6] = E_zx
GreenLagrangeStrain[7] = E_zy
GreenLagrangeStrain[8] = E_zz
For interpretation of these components:
E_xx positive, for example, indicates stretching in the x direction
---------- ^ y
| | |
<--- | | ---> E_xx > 0 |
| | ------> x
----------
E_xy positive, for example, indicates shearing in the xy plane
------>
---------- ^ y
| | | ^ |
| | | | E_xy > 0 |
v | | | ------> x
----------
<-------
'''
## --- Get cell points array in REFERENCE configuration --- ##
# Get list of point indices for each cell
cell_indices = surf.faces
# Make sure all triangles
assert not cell_indices.size % 4 and np.all(cell_indices.reshape(-1, 4)[:,0] == 3)
# Reshape to get ncells x 3 array of nodal indices
cell_node_ids = cell_indices.reshape(-1, 4)[:,1:4].ravel()
cell_node_ids = cell_node_ids.reshape(-1, 3) # ncells x 3
#print(cell_node_ids)
# Get an array of points for each cell
# cell_nodes[cell_id, node_id, dim]. cell_id = [0, ncells-1], node_id = [0,2], dim = [0,2]
cell_nodes = surf.points[cell_node_ids] # ncells x 3 x 3
# Reference configuration nodal positions for each cell
X = cell_nodes
## --- Get cell points array in CURRENT configuration --- ##
# Warp geometry by displacement
surf_cur = surf.warp_by_vector('Displacement', factor = 1)
# Get list of point indices for each cell
cell_indices = surf_cur.faces
# Make sure all triangles
assert not cell_indices.size % 4 and np.all(cell_indices.reshape(-1, 4)[:,0] == 3)
# Reshape to get ncells x 3 array of nodal indices
cell_node_ids = cell_indices.reshape(-1, 4)[:,1:4].ravel()
cell_node_ids = cell_node_ids.reshape(-1, 3) # ncells x 3
#print(cell_node_ids)
# Get an array of points for each cell
# cell_nodes[cell_id, node_id, dim]. cell_id = [0, ncells-1], node_id = [0,2], dim = [0,2]
cell_nodes = surf_cur.points[cell_node_ids] # ncells x 3 x 3
# Reference configuration nodal positions for each cell
x = cell_nodes
ncells = surf.n_faces
## --- Compute deformation gradient --- ##
# Construct A1, A2, N, and a1, a2, N
A1 = X[:, 2-1] - X[:, 1-1] # -1 to switch to 0-indexing
A2 = X[:, 3-1] - X[:, 1-1]
N = np.cross(A1, A2)
N = N / np.linalg.norm(N, axis=1)[:, np.newaxis]
# Check
#surf.compute_normals(point_normals=False, inplace=True)
#normals = surf['Normals']
#print(np.linalg.norm(N - normals))
a1 = x[:, 2-1] - x[:, 1-1]
a2 = x[:, 3-1] - x[:, 1-1]
n = np.cross(a1, a2)
n = n / np.linalg.norm(n, axis=1)[:, np.newaxis]
# Form AAN and aan matrices
AAN = np.empty((ncells, 3, 3))
AAN[:, :, 0] = A1
AAN[:, :, 1] = A2
AAN[:, :, 2] = N
aan = np.empty((ncells, 3, 3))
aan[:, :, 0] = a1
aan[:, :, 1] = a2
aan[:, :, 2] = n
# Compute deformation gradient from aan = F * AAN -> F = aan * inv(AAN)
F = aan @ np.linalg.inv(AAN)
#print(F.shape)
# Compute Green-Lagrange strain tensor
FT = np.transpose(F, (0, 2, 1))
E = 1/2 * (FT @ F - np.identity(3))
#print(E.shape)
# Populate surf with GL strain components
E_vec = np.empty((ncells, 9))
E_vec[:,0] = E[:, 0, 0]
E_vec[:,1] = E[:, 0, 1]
E_vec[:,2] = E[:, 0, 2]
E_vec[:,3] = E[:, 1, 0]
E_vec[:,4] = E[:, 1, 1]
E_vec[:,5] = E[:, 1, 2]
E_vec[:,6] = E[:, 2, 0]
E_vec[:,7] = E[:, 2, 1]
E_vec[:,8] = E[:, 2, 2]
surf.cell_data["GreenLagrangeStrain"] = E_vec
return surf