-
Notifications
You must be signed in to change notification settings - Fork 13
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
Ideas for SolidBodyContact
#919
Comments
Elastic supportA part of the boundary Re-formulated by integration over the undeformed area: This weak form requires both the deformation gradient and the displacement values. What is the most efficient way to implement this (without
|
Next improvement. Quite a lot of overhead because each and every Gauss-point on the active edges are tested against each other. Also, the contact stiffness seems to depend on the number of cells (Gauss-points). codeimport felupe as fem
import numpy as np
import pypardiso
mesh = fem.MeshContainer(
[fem.Cube(n=3), fem.Cube(n=4).translate(1.05, 0)], merge=True
).stack()
region = fem.RegionHexahedron(mesh)
edges = [
fem.RegionHexahedronBoundary(mesh, mask=mesh.x == 1),
fem.RegionHexahedronBoundary(mesh, mask=mesh.x == 1.05),
]
field = fem.Field(region, dim=3).as_container()
class SolidBodyContact:
def __init__(self, region, other_region, stiffness=1.0):
cells = np.tile(region.mesh.cells, (other_region.mesh.ncells, 1))
other_cells = np.tile(other_region.mesh.cells, (1, region.mesh.ncells))
self.region = region.copy(mesh=region.mesh.copy(cells=cells))
self.other_region = region.copy(
mesh=region.mesh.copy(cells=other_cells.reshape(cells.shape))
)
(
self.region.dA,
self.region.dV,
self.region.normals,
self.region.tangents,
) = self.region._init_faces()
(
self.other_region.dA,
self.other_region.dV,
self.other_region.normals,
self.other_region.tangents,
) = self.other_region._init_faces()
self.field = fem.Field(self.region, dim=3).as_container()
self.other_field = fem.Field(self.other_region, dim=3).as_container()
self.stiffness = stiffness
self.assemble = fem.mechanics.Assemble(
self.vector, self.matrix, multiplier=-1.0
)
self.results = fem.mechanics.Results()
def fun(self, F, u, u_ext, K, N):
import tensortrax as tr
import tensortrax.math as tm
x = F if isinstance(F, tr.Tensor) else u if isinstance(u, tr.Tensor) else u_ext
N = tm.array(N, shape=(3,), like=x)
t = tm.linalg.det(F) * tm.transpose(tm.linalg.inv(F)) @ N
n = t / tm.sqrt(tm.sum(t**2, axis=0))
un = -(u_ext - u) @ n
gn = tm.if_else(un.x > 0, un, 0 * un)
return -K * gn * t
def extract(self, field=None):
if field is not None:
self.field.link(field)
self.other_field.link(field)
F = self.field.extract()[0]
u = self.field.extract(grad=False)[0]
u_ext = self.other_field.extract(grad=False)[0]
return F, u, u_ext
def vector(self, field=None, **kwargs):
import tensortrax as tr
F, u, u_ext = self.extract(field=field)
K, N = self.stiffness, self.field.region.normals
vector = fem.IntegralForm(
fun=[tr.function(self.fun, ntrax=2)(F, u, u_ext, K, N)],
v=self.field,
dV=self.field.region.dV,
grad_v=[False],
).assemble()
return vector
def matrix(self, field=None, **kwargs):
import tensortrax as tr
F, u, u_ext = self.extract(field=field)
K, N = self.stiffness, self.field.region.normals
matrix = (
fem.IntegralForm(
fun=[tr.jacobian(self.fun, ntrax=2, wrt=1)(F, u, u_ext, K, N)],
v=self.field,
dV=self.field.region.dV,
u=self.field,
grad_v=[False],
grad_u=[False],
).assemble()
+ fem.IntegralForm(
fun=[tr.jacobian(self.fun, ntrax=2, wrt=2)(F, u, u_ext, K, N)],
v=self.field,
dV=self.field.region.dV,
u=self.other_field,
grad_v=[False],
grad_u=[False],
).assemble()
+ fem.IntegralForm(
fun=[tr.jacobian(self.fun, ntrax=2, wrt=0)(F, u, u_ext, K, N)],
v=self.field,
dV=self.field.region.dV,
u=self.field,
grad_v=[False],
grad_u=[True],
).assemble()
)
return matrix
boundaries = fem.BoundaryDict(
move=fem.Boundary(field[0], fx=2.05), fixed=fem.Boundary(field[0], fx=0)
)
umat = fem.NeoHooke(mu=1)
solid = fem.SolidBodyNearlyIncompressible(umat=umat, field=field, bulk=5000)
support = SolidBodyContact(*edges[::], stiffness=1.5)
support2 = SolidBodyContact(*edges[::-1], stiffness=1.5)
move = fem.math.linsteps([0, -0.3], num=10, axis=0, axes=3)
ramp = {boundaries["move"]: move}
step = fem.Step(items=[solid, support, support2], ramp=ramp, boundaries=boundaries)
job = fem.Job(steps=[step])
job.evaluate(solver=pypardiso.spsolve)
solid.plot("Displacement", opacity=0.9).show() |
see also adtzlr/matadi#143 import felupe as fem
import numpy as np
mesh = fem.Cube(n=3)
region = fem.RegionHexahedron(mesh)
field = fem.FieldsMixed(region, n=1)
u = fem.Field(fem.RegionHexahedronBoundary(mesh, mask=mesh.x == 1), dim=3)
fieldb = u & u
fieldb.link(field)
import matadi as mat
from matadi.math import det, inv, gradient, trace, zeros, eye, sqrt, sum1, dot, DM
# def fun(x):
# F, u, statevars = x[0], x[1], x[-1]
# return [det(F) * u[0], statevars]
# x = [mat.Variable("F", 3, 3), mat.Variable("x", 3), mat.Variable("z", 1)]
# umat = mat.Material(x, fun, statevars=1)
# statevars = field.extract()[-1][0]
# dWdF, statevars_new = umat.gradient([*field.extract(), statevars])
# dWdF, dWdu, statevars = umat.hessian([*field.extract(), statevars])
# def fun(x):
# F, p, J, statevars = x[0], x[1], x[2], x[-1]
# W = (
# mat.models.neo_hooke(F * (J / det(F))**(1 / 3), C10=0.5)
# + p * (det(F) - J)
# + mat.models.volumetric(J, bulk=5000)
# )
# return gradient(W, F), gradient(W, p), gradient(W, J), statevars
# x = [mat.Variable("F", 3, 3), mat.Variable("p"), mat.Variable("J"), mat.Variable("z")]
def fun(x):
F, u, N = x[0], x[1], x[-1]
p = -1.0
return zeros(3, 3), -p * det(F) * inv(F).T @ N, N
x = [mat.Variable("F", 3, 3), mat.Variable("u", 3), mat.Variable("N", 3)]
umatb = mat.MaterialTensor(x, fun, statevars=1, triu=True)
boundaries = dict(fixed=fem.Boundary(field[0], fx=0))
umat = fem.NeoHooke(mu=1, bulk=2)
solid = fem.SolidBody(umat=umat, field=field)
def apply(x):
if len(x) == 2:
return sum(x)
else:
return x[0] + x[1].T + x[2]
solidb = fem.SolidBody(
umat=umatb,
field=fieldb,
block=False,
apply=apply,
statevars=u.region.normals,
multiplier=-1.0,
)
v = u.as_container()
v.link(field)
solidc = fem.SolidBodyPressure(v, pressure=-1.0)
step = fem.Step(items=[solid, solidb], boundaries=boundaries)
job = fem.Job(steps=[step])
job.evaluate(verbose=2, tol=1e3)
ax = solid.imshow("Principal Values of Cauchy Stress", factor=1) |
Even with tensortrax, there is something strange here. import felupe as fem
import numpy as np
mesh = fem.Cube(n=3)
region = fem.RegionHexahedron(mesh)
field = fem.FieldsMixed(region, n=1)
u = fem.Field(fem.RegionHexahedronBoundary(mesh, mask=mesh.x == 1), dim=3)
fieldb = u & u
fieldb.link(field)
import tensortrax.math as tm
import tensortrax as tr
N = u.region.normals
def stress(x, p, N):
F, u = x[:2]
def fun(F, u, p, N):
J = tm.linalg.det(F)
invF = tm.linalg.inv(F)
return -p * J * invF.T @ tm.array(N, like=F[0])
dWdu = tr.function(fun, wrt=0, ntrax=2)(F, u, p=p, N=N)
return 0 * F, dWdu, x[-1]
def elasticity(x, p, N):
F, u = x[:2]
def fun(F, u, p, N):
J = tm.linalg.det(F)
invF = tm.linalg.inv(F)
return -p * J * invF.T @ tm.array(N, like=F[0])
d2WdudF = tr.jacobian(fun, wrt=0, ntrax=2)(F, u, p=p, N=N)
return np.zeros((3, 3, 3, 3, 4, 4)), d2WdudF, np.zeros((3, 3, 4, 4))
umatb = fem.Material(stress, elasticity, p=-1.0, N=u.region.normals)
boundaries = dict(fixed=fem.Boundary(field[0], fx=0))
umat = fem.NeoHooke(mu=1, bulk=2)
solid = fem.SolidBody(umat=umat, field=field)
solidb = fem.SolidBody(
umat=umatb,
field=fieldb,
block=False,
apply=sum,
multiplier=-1.0,
)
v = u.as_container()
v.link(field)
solidc = fem.SolidBodyPressure(v, pressure=-1.0)
step = fem.Step(items=[solid, solidb], boundaries=boundaries)
job = fem.Job(steps=[step])
job.evaluate(verbose=2, tol=1e3)
ax = solid.imshow("Principal Values of Cauchy Stress", factor=1) |
import felupe as fem
import numpy as np
import tensortrax.math as tm
import tensortrax as tr
import pypardiso
mesh = fem.MeshContainer(
[fem.Cube(n=3), fem.Cube(n=4).translate(1.05, 0)], merge=True
).stack()
region = fem.RegionHexahedron(mesh)
edges = [
fem.Field(fem.RegionHexahedronBoundary(mesh, mask=mesh.x == 1), dim=3),
fem.Field(fem.RegionHexahedronBoundary(mesh, mask=mesh.x == 1.05), dim=3),
]
field = fem.Field(region, dim=3).as_container()
def fun(F, u, N):
J = tm.linalg.det(F)
invF = tm.linalg.inv(F)
p = 5000 * (J - 1)
return -p * J * invF.T @ tm.array(N, like=F[0])
def stress(x, N):
F, u = x[:2]
dWdu = tr.function(fun, wrt=0, ntrax=2)(F, u, N=N)
return None, dWdu, x[-1]
def elasticity(x, N):
F, u = x[:2]
d2WdudF = tr.jacobian(fun, wrt=0, ntrax=2)(F, u, N=N)
return None, None, d2WdudF, None
boundaries = fem.BoundaryDict(
move=fem.Boundary(field[0], fx=2.05), fixed=fem.Boundary(field[0], fx=0)
)
umat = fem.NeoHooke(mu=1)
solid = fem.SolidBodyNearlyIncompressible(umat=umat, field=field, bulk=5000)
contact_1 = fem.SolidBody(
umat=fem.Material(stress, elasticity, N=edges[0].region.normals),
field=edges[0] & edges[0],
block=False,
apply=sum,
multiplier=-1.0,
)
contact_2 = fem.SolidBody(
umat=fem.Material(stress, elasticity, N=edges[1].region.normals),
field=edges[1] & edges[1],
block=False,
apply=sum,
multiplier=-1.0,
)
move = fem.math.linsteps([0, -0.3], num=10, axis=0, axes=3)
ramp = {boundaries["move"]: move}
step = fem.Step(items=[solid, contact_1, contact_2], ramp=ramp, boundaries=boundaries)
job = fem.Job(steps=[step])
job.evaluate(solver=pypardiso.spsolve)
solid.plot("Displacement", opacity=0.9).show() |
A contact between two solid bodies should be defined in the most high-level form as contact = SolidBodyContact(solid, other_solid) The displacement field of the solid bodies should be moved to the (free) boundaries to obtain and assemble the required weak-forms. and For this kind of formulation, equal quadrature schemes and element formulations are required for the contact pairs. |
This issue serves as a collection of ideas on how to implement a contact between two solid bodies (once more).
Requirements
item
, likeSolidBody
with methods toassemble
the force vector- and the stiffness matrixRoadmap
The text was updated successfully, but these errors were encountered: