Hi, I have adapted the code from demo_elastodynamics.py in order to make a similar solver in dolfinx. However, it runs much slower than the legacy version.
In the current state of the code (see below), it takes 40 secs to run for a mesh with 500 cells and 50 timesteps. This is worrying because A) it is much slower than the legacy version of effectively the same code and B) eventually I want to multiply both of these parameters by 100.
I am new to the use of FEniCS/dolfinx but I assume a better problem-solver configuration is the solution? Any help on this would be appreciated. ![]()
import numpy as np
import matplotlib.pyplot as plt
from mpi4py import MPI
from petsc4py import PETSc
from dolfinx import fem, mesh,default_scalar_type
from dolfinx.io import XDMFFile
from dolfinx import *
import ufl
import dolfinx.fem.petsc, dolfinx.nls.petsc
import time
start_time = time.time()
# Create mesh and function space
L = 1.0 # Length of the rod
nx, ny, nz = 20, 5, 5 # Number of elements in x, y, and z directions
domain = mesh.create_box(MPI.COMM_WORLD, [np.array([0, 0, 0]), np.array([L, 0.1, 0.1])], [nx, ny, nz], cell_type=mesh.CellType.hexahedron)
V = fem.VectorFunctionSpace(domain, ("CG", 1))
fdim = domain.topology.dim - 1
def clamped_boundary(x):
return np.isclose(x[0], 0) #|np.isclose(x[0], L)
boundary_facets = mesh.locate_entities_boundary(domain, fdim, clamped_boundary)
u_D = np.array([0, 0, 0], dtype=default_scalar_type)
bc = fem.dirichletbc(u_D, fem.locate_dofs_topological(V, fdim, boundary_facets), V)
# Elasticity constants
E = 1.0e6
nu = 0.3
mu = E / (2.0 * (1 + nu))
lmbda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu))
# Time integration parameters
am = 0.2
af = 0.4
g = 0.5 + af - am
alpha_m = fem.Constant(domain, am)
alpha_f = fem.Constant(domain, af)
gamma = fem.Constant(domain, g)
beta = fem.Constant(domain, (g + 0.5)**2 / 4.0)
rho = 1.0
eta_m = 0.0
eta_k = 0.0
T = 1.0
Nsteps = 50
dt = fem.Constant(domain, T / Nsteps)
# Functions and fields
u_ = ufl.TestFunction(V)
u = fem.Function(V, name='Displacement')
u_old = fem.Function(V)
v_old = fem.Function(V)
a_old = fem.Function(V)
# External load
B = fem.Constant(domain, PETSc.ScalarType((0.0, 0.0, -1.0))) # Applied load in the z-direction
# Measures
dx = ufl.Measure("dx", domain=domain)
# GA method functions
def sigma(r):
return lmbda*ufl.nabla_div(r)*ufl.Identity(len(r)) + 2*mu*ufl.sym(ufl.grad(r))
def m(u,u_):
return rho*ufl.inner(u,u_)*dx
def k(u,u_):
return ufl.inner(sigma(u),ufl.grad(u_))*dx
def c(u,u_):
return eta_m*m(u,u_) + eta_k*k(u,u_)
#update variables
def update_a(u, u_old, v_old, a_old, ufl_=True):
if ufl_:
dt_ = dt
beta_ = beta
else:
dt_ = dt
beta_ = beta.value
return (u-u_old-dt_*v_old)/beta_/dt_**2 - (1-2*beta_)/2/beta_*a_old
def update_v(a,u_old,v_old,a_old,ufl_=True):
if ufl_:
dt_ = dt
gamma_ = gamma
else:
dt_ = dt
gamma_ = gamma.value
return v_old + dt_*((1-gamma_)*a_old + gamma_*a)
def update_fields(u,u_old,v_old,a_old):
u_vec,u0_vec = u.x.array[:],u_old.x.array[:]
v0_vec,a0_vec = v_old.x.array[:],a_old.x.array[:]
a_vec = update_a(u_vec,u0_vec,v0_vec,a0_vec,ufl_=False)
v_vec = update_v(a_vec,u0_vec,v0_vec,a0_vec,ufl_=False)
v_old.x.array[:] = v_vec
a_old.x.array[:] = a_vec
u_old.x.array[:] = u_vec
def avg(x_old,x_new,alpha):
return alpha*x_old + (1-alpha)*x_new
a_new = update_a(u,u_old,v_old,a_old,ufl_=True)
v_new = update_v(a_new,u_old,v_old,a_old,ufl_=True)
# Problem setup
res = m(avg(a_old,a_new,alpha_m),u_) + c(avg(v_old,v_new,alpha_f),u_) + k(avg(u_old,u,alpha_f),u_) - rho*ufl.inner(B,u_)*dx #residual to solve
problem = fem.petsc.NonlinearProblem(res,u,[bc])
# Solver setup
solver = nls.petsc.NewtonSolver(MPI.COMM_WORLD, problem)
solver.atol = 1e-8
solver.rtol = 1e-8
solver.convergence_criterion = "incremental"
# Quantities to track
times = np.linspace(0, T, Nsteps + 1)
# Create file
with XDMFFile(domain.comm, "rod_dbc0.xdmf", "w") as xdmf_file:
xdmf_file.write_mesh(domain)
xdmf_file.write_function(u, 0)
# Write function at each time step
for i in range(Nsteps):
t = times[i + 1]
print("Time = %s" % t )
num_its, converged = solver.solve(u)
assert converged
u.x.scatter_forward()
# Update old fields with new quantities
update_fields(u, u_old, v_old, a_old)
# Write the function to the XDMF file
xdmf_file.write_function(u, t)
print("--- %s seconds runtime ---" % (time.time() - start_time))