This is the whole code:
import dolfinx
from dolfinx import mesh, fem, plot
from mpi4py import MPI
import basix
import numpy as np
from petsc4py import PETSc
import ufl
from dolfinx.fem.petsc import LinearProblem
from dolfinx.fem import dirichletbc, locate_dofs_topological
from ufl import Measure, SpatialCoordinate, TestFunctions, TrialFunctions, div, inner, dot
import pyvista as pv
# Material properties
E = 2000000.0 # Young's modulus in PSI (for structural wood)
nu = 0.299999 # Poisson's ratio (for structural wood)
L = 72 # Beam length in inches
W = 4.0 # Beam width in inches
w = 100.0 # Distributed load in lbs/inch
# Define the mesh with tetrahedral cells
domain = mesh.create_box(MPI.COMM_WORLD, [np.array([0, 0, 0]), np.array([L, W, W])],
[20, 6, 6], cell_type=mesh.CellType.tetrahedron)
# Define elements for displacements (3 DOF) and rotations (3 DOF)
elem_disp = basix.ufl.element("Lagrange", domain.topology.cell_name(), 1, shape=(3,))
elem_rot = basix.ufl.element("Lagrange", domain.topology.cell_name(), 1, shape=(3,))
elem_mixed = basix.ufl.mixed_element([elem_disp, elem_rot])
V = fem.functionspace(domain, elem_mixed)
VV = fem.functionspace(domain, elem_disp)
# Strain tensor as a symmetric gradient of the displacement field
def epsilon(u):
return 0.5 * (ufl.grad(u) + ufl.grad(u).T)
# Stress tensor derived from Hooke's law
def sigma(u):
lambda_ = E * nu / ((1 + nu) * (1 - 2 * nu)) # First Lamé parameter
mu = E / (2 * (1 + nu)) # Shear modulus (second Lamé parameter)
return lambda_ * ufl.tr(epsilon(u)) * ufl.Identity(3) + 2 * mu * epsilon(u)
# Define trial and test functions for displacements (u) and rotations (theta)
(u, theta) = TrialFunctions(V)
(v, tau) = TestFunctions(V)
# Weak form for the elasticity problem (displacement part)
a_disp = inner(sigma(u), epsilon(v)) * ufl.dx
# Rotational-related terms (rotations theta and tau)
a_rot = inner(theta, tau) * ufl.dx
# Coupling terms between displacement and rotation (optional)
a_coupling = inner(theta, v) * ufl.dx + inner(u, tau) * ufl.dx
# Final variational form: combining displacements, rotations, and coupling
a = a_disp + a_rot + a_coupling
# Get subspace of V for displacements
V0 = V.sub(0)
Q, _ = V0.collapse()
fdim = domain.topology.dim - 1
# Locate the entities where x=0 (left boundary) and x=L (right boundary)
def locate_left_boundary(x):
return np.isclose(x[0], 0.0)
def locate_right_boundary(x):
return np.isclose(x[0], L)
facets_left = mesh.locate_entities_boundary(domain, fdim, locate_left_boundary)
facets_right = mesh.locate_entities_boundary(domain, fdim, locate_right_boundary)
# Apply boundary condition at the left boundary where x = 0 (clamping)
dofs_left = locate_dofs_topological(V0, fdim, facets_left)
u_left = fem.Function(Q)
with u_left.vector.localForm() as loc:
loc.set(0.0) # Zero displacement in all directions
bc_left = dirichletbc(u_left, dofs_left)
# Apply boundary condition at the right boundary where x = L (clamping)
dofs_right = locate_dofs_topological(V0, fdim, facets_right)
u_right = fem.Function(Q)
with u_right.vector.localForm() as loc:
loc.set(0.0) # Zero displacement in all directions
bc_right = dirichletbc(u_right, dofs_right)
# Compute the distributed load per node and apply it
nodes = domain.geometry.x
num_nodes = len(nodes)
# Create force function f in the same mixed space V
f = fem.Function(V)
# Compute the distributed load per node and apply it
nodes = domain.geometry.x
num_nodes = len(nodes)
# Assign distributed force (downward in -y) and moment (around x-axis)
I = (W**4) / 12 # Moment of inertia for a rectangular cross-section
# Compute the number of degrees of freedom per node
dofs_per_node = 6
# Loop over all nodes to apply the distributed load and moment
for i, node in enumerate(nodes):
fy = -w * L / num_nodes # Distributed load down the -y axis
y = node[1] # y-coordinate of the node
mx = -(w * abs(y) * L**2) / (2 * I) # Moment around x-axis
# Apply fy to the displacement component in y and mx to the rotational component around x
f.vector.setValue(i * dofs_per_node + 1, fy) # Displacement force in y
f.vector.setValue(i * dofs_per_node + 3, mx) # Rotational moment around x
# Define the right-hand side (RHS) for the variational problem
l = inner(f, v) * ufl.dx # Adjust RHS to match the shape of the test function v
# Create the linear problem and solve it
#problem = LinearProblem(a, l, bcs=[bc_left, bc_right])
#uh = problem.solve()
# Save the solution to a CSV file
#np.savetxt('U.csv', uh.x.array)
#print('done...')
with traceback:
Traceback (most recent call last):
File "/home/prusso/dolfinx-beam_w/compute_displacements2.py", line 115, in <module>
l = inner(f, v) * ufl.dx # Adjust RHS to match the shape of the test function v
File "/home/prusso/spack/var/spack/environments/fenicsx-env/.spack-env/view/lib/python3.10/site-packages/ufl/operators.py", line 213, in inner
return Inner(a, b)
File "/home/prusso/spack/var/spack/environments/fenicsx-env/.spack-env/view/lib/python3.10/site-packages/ufl/tensoralgebra.py", line 166, in __new__
raise ValueError(f"Shapes do not match: {ufl_err_str(a)} and {ufl_err_str(b)}")
ValueError: Shapes do not match: <Coefficient id=128873217154304> and <ListTensor id=128873219570112>
If I understand it, there is a shape problem building the linear form after using the force vector this way.
This is the desired algorithm so far:
(Set a distributed load, small cursive w, and a moment of force for each node):
# Loop over all nodes to apply the distributed load and moment
for i, node in enumerate(nodes):
fy = -w * L / num_nodes # Distributed load down the -y axis
y = node[1] # y-coordinate of the node
mx = -(w * abs(y) * L**2) / (2 * I) # Moment around x-axis
# Apply fy to the displacement component in y and mx to the rotational component around x
f.vector.setValue(i * dofs_per_node + 1, fy) # Displacement force in y
f.vector.setValue(i * dofs_per_node + 3, mx) # Rotational moment around x
As an alternative to troubleshoot the problem I had also tried this way:
# Get subspace of V for displacements
V0 = V.sub(0)
Q, _ = V0.collapse()
fdim = domain.topology.dim - 1
# Compute the distributed load per node and apply it
nodes = domain.geometry.x
num_nodes = len(nodes)
f_disp = fem.Function(Q)
# Loop over all nodes to apply the distributed load (displacement force)
for i, node in enumerate(nodes):
fy = -w * L / num_nodes # Distributed load down the -y axis
f_disp.vector.setValue(i * 3 + 1, fy) # Displacement force in y
# Define the right-hand side (RHS) for the variational problem
l = inner(f_disp, v[0]) * ufl.dx # Adjust RHS to match the shape of the test function v
which leads to a different traceback:
Traceback (most recent call last):
File "/home/prusso/dolfinx-beam_w/compute_displacements3.py", line 99, in <module>
l = inner(f_disp, v[0]) * ufl.dx # Adjust RHS to match the shape of the test function v
File "/home/prusso/spack/var/spack/environments/fenicsx-env/.spack-env/view/lib/python3.10/site-packages/ufl/operators.py", line 213, in inner
return Inner(a, b)
File "/home/prusso/spack/var/spack/environments/fenicsx-env/.spack-env/view/lib/python3.10/site-packages/ufl/tensoralgebra.py", line 166, in __new__
raise ValueError(f"Shapes do not match: {ufl_err_str(a)} and {ufl_err_str(b)}")
ValueError: Shapes do not match: <Coefficient id=132880029505664> and <Indexed id=132880029444000>
What is going wrong here? How can I set up the desired algorithm to work as far as setting the forces and moments for this type of problem?