I am trying to solve a reliability analysis problem where the FEA needs to be solved many times while changing the mesh and some material parameters every time the problem is setup and solved. All the code works well when I run it once, but any time I try and run it multiple times in a loop or execute the python script multiple times in a row from a seperate python file, at the end of the loops it gives me a memory error from PETSC, specifically:
“[0]PETSC ERROR: Caught signal number 11 SEGV: Segmentation Violation, probably memory access out of range”
When I check the container memory usage its well under what it can use (~4GB vs 120 available). I tried using the python garbage collector at the end of each analysis and deleting all variables using the “del” keyword, however that caused the same PETSC error to occur, but after the first loop instead of the last. I also tried using the
Any help to solve this would be greatly appreciated. I made a simplified code based on one of the tutorials for linear elasticity and included it below, I am using Fenicsx 0.8.0 and Ubuntu 18.04.6 for the docker container.
from dolfinx import log, mesh, fem, default_scalar_type
from dolfinx.fem.petsc import NonlinearProblem
from dolfinx.nls.petsc import NewtonSolver
from mpi4py import MPI
from petsc4py import PETSc
import ufl
import numpy as np
for i in range(0,5):
# physical variables
N = 20
L = 1.0
D = 2*L
E = default_scalar_type(10.0)
nu = default_scalar_type(0.3)
DmatPlaneStress = E/(1-nu*nu)*ufl.as_matrix([[1,nu,0],[nu,1,0],[0,0,(1-nu)]])
Dmat = DmatPlaneStress
# meshing variables
elementOrder = 2
# create mesh
domain = mesh.create_rectangle(
MPI.COMM_WORLD,
[[-L, -D], [L, 0]],
[N, int(round(N*D/L))],
cell_type=mesh.CellType.triangle,
)
# find the top surface
def top_surface(x):
return np.isclose(x[1],0.0)
# define other boundaries
def bottom(x):
return np.isclose(x[1], -D)
# locate all exterior faces (for later boolean operations)
tdim = domain.topology.dim
fdim = tdim - 1
domain.topology.create_connectivity(fdim, tdim)
boundary_facets_all = mesh.exterior_facet_indices(domain.topology)
# locate different regions of facets where the boundary conditions will be applied
topFacets = mesh.locate_entities_boundary(domain, fdim, top_surface)
bottomZ_facets = mesh.locate_entities_boundary(domain, fdim, bottom)
# define the mesh marker for each facets on the mesh
num_facets = domain.topology.index_map(fdim).size_local
markers = np.zeros(num_facets, dtype=np.int32)
top_mark,bottomZ_mark = 1,2
markers[topFacets],markers[bottomZ_facets] = top_mark,bottomZ_mark
facet_marker = mesh.meshtags(domain, fdim, np.arange(num_facets, dtype=np.int32), markers)
# define the function space
V = fem.functionspace(domain, ("Lagrange", elementOrder, (domain.geometry.dim,)))
# define variables for energy density function
mu = fem.Constant(domain, E / (2 * (1 + nu)))
lmbda = fem.Constant(domain, E * nu / ((1 + nu) * (1 - 2 * nu)))
# define the displacment boundary condition numerically, then assign them to the boundary facets
u_fixedAll = np.array([0, 0], dtype=default_scalar_type)
bc_bottom = fem.dirichletbc(u_fixedAll, fem.locate_dofs_topological(V, fdim, facet_marker.find(bottomZ_mark)),V)
bcs = [bc_bottom]
# define the traction to be 0
T = fem.Constant(domain, default_scalar_type((0, 5)))
# define the constant body force value
f = fem.Constant(domain, default_scalar_type((0, 0)))
def TensorToVec(m):
return ufl.as_vector([m[0,0],m[1,1],.5*m[0,1]+.5*m[1,0]])
def VecToTensor(vec):
return ufl.as_matrix([[vec[0],vec[2]],[vec[2],vec[1]]])
# return the strain from the displacement symbolically
def epsilon(z):
return ufl.sym(ufl.grad(z))
# return the stress from the displacement symbolically
def sigma(z):
sig = VecToTensor(ufl.dot(Dmat,TensorToVec(epsilon(z))))
return sig
# define the test and solution space
v = ufl.TestFunction(V)
u = fem.Function(V)
du = ufl.TrialFunction(V)
# define the integration measure with traction integral over all facets with value 2 and set quadrature degree to 4
ds = ufl.Measure("ds", domain=domain, subdomain_data=facet_marker)
dx = ufl.Measure("dx", domain=domain)
# calculate the residual of the equations
residual = ufl.inner(sigma(u), epsilon(v)) * dx - ufl.dot(f, v) * dx - ufl.dot(T, v) * ds(top_mark)
# define tangent tensor
a = ufl.derivative(residual, u, du) # for taking the derivative with respect to a function
jit_options ={"cffi_extra_compile_args":["-O3","-ffast-math"]}
problem = NonlinearProblem(residual, u, bcs, a,jit_options=jit_options)
solver = NewtonSolver(MPI.COMM_WORLD, problem)
solver.convergence_criterion = "incremental"
solver.rtol = 1e-8
solver.atol = 1e-8
solver.max_it = 100
solver.report = True
ksp = solver.krylov_solver
opts = PETSc.Options()
option_prefix = ksp.getOptionsPrefix()
opts[f"{option_prefix}ksp_type"] = "preonly"
opts[f"{option_prefix}pc_type"] = "lu"
opts[f"{option_prefix}pc_factor_mat_solver_type"] = "mumps"
ksp.setFromOptions()
totSteps = 2
dispHist = np.zeros(shape=[totSteps-1])
loadHist = np.zeros(shape=[totSteps-1])
# solve the problem over several timesteps increasing the penalization slowly
log.set_log_level(log.LogLevel.INFO)
for n in range(1, totSteps):
try:
(iter, converged) = solver.solve(u)
except: # Break the loop if solver fails
print( "Ended Early")
break
print("max displacement: " + str(np.max(np.abs(u.x.array))))