Hi. I am trying to implement a model for a clamped beam based on the tutorial https://comet-fenics.readthedocs.io/en/latest/demo/elasticity/2D_elasticity.py.html , but with a spatially varying elastic modulus (having the Lame parameters be defined as a spatial field that varies along the beam length. I am working with the code below:
from dolfin import *
import matplotlib.pyplot as plt
import numpy as np
L = 25.
H = 1.
Nx = 250
Ny = 10
mesh = RectangleMesh(Point(0., 0.), Point(L, H), Nx, Ny, 'crossed')
def eps(v):
return sym(grad(v))
E = Constant(1e5)
nu = Constant(0.3)
model = "plane_stress"
mu = E/2/(1+nu)
lmbda = E*nu/(1+nu)/(1-2*nu)
if model == "plane_stress":
lmbda = 2*mu*lmbda/(lmbda+2*mu)
rho_g = 1e-3
f = Constant((0, -rho_g))
V = VectorFunctionSpace(mesh, 'Lagrange', degree=1)
du = TrialFunction(V)
u_ = TestFunction(V)
d2v = dof_to_vertex_map(V)
def lmbda_x(coords):
return 30000 + 10.0 * coords[0]
lmbda_func = Function(V)
lmbda_vals = np.apply_along_axis(lmbda_x, 1, mesh.coordinates())
# Want Lame parameter to be the same in x and y direction, so copy the values once.
lmbda_vals = np.hstack([lmbda_vals, lmbda_vals])
lmbda_func.vector().set_local(lmbda_vals[d2v])
def sigma(v):
# return lmbda*tr(eps(v))*Identity(2) + 2.0*mu*eps(v)
return lmbda_func * tr(eps(v)) * Identity(2) + 2.0*mu*eps(v)
a = inner(sigma(du), eps(u_))*dx
l = inner(f, u_)*dx
def left(x, on_boundary):
return near(x[0], 0.)
bc = DirichletBC(V, Constant((0.,0.)), left)
u = Function(V, name="Displacement")
solve(a == l, u, bc)
plt.figure()
plot(1e3*u, mode="displacement")
plt.savefig('test.png')
print("Maximal deflection:", -u(L,H/2.)[1])
print("Beam theory deflection:", float(3*rho_g*L**4/2/E/H**3))
However, the following error occurs:
Invalid ranks 1 and 2 in product.
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/IPython/core/interactiveshell.py", line 3319, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-120-463cfb80453d>", line 5, in <module>
a = inner(sigma(du), eps(u_))*dx
File "<ipython-input-120-463cfb80453d>", line 3, in sigma
return lmbda_func*Identity(2) * tr(eps(v))* Identity(2) + 2.0*mu*eps(v)
File "/usr/local/lib/python3.6/dist-packages/ufl/exproperators.py", line 193, in _mul
return _mult(self, o)
File "/usr/local/lib/python3.6/dist-packages/ufl/exproperators.py", line 167, in _mult
error("Invalid ranks {0} and {1} in product.".format(r1, r2))
File "/usr/local/lib/python3.6/dist-packages/ufl/log.py", line 172, in error
raise self._exception_type(self._format_raw(*message))
ufl.log.UFLException: Invalid ranks 1 and 2 in product.
which suggests that lmbda_func
is of rank 1 and tr(eps(v)) * Identity(2)
is of rank 2 (although I didn’t manage to check the latter with .rank()
). If I replace the definition of lmbda_x
with the commented line (using a constant Lame parameter), it works nicely.
My question is then: how can I convert the rank 1 lmbda_func
into an appropriate form to achieve the spatially varying property? Thanks in advance.