Memory allocation error in creating form

Hello, Greetings!

I am getting error of Unable to allocate 75.2 GiB for an array with shape (100489, 100489) and data type float64. I am missing something fundamental in creating form with trial function dv. With this I am unable to create petsc.assemble_matrix of this bilinear form. Kindly help me to remove this high memory allocation error. Any help is highly appreciated!

The shell_curved_2elem.msh file used is:

$MeshFormat
2.2 0 8
$EndMeshFormat
$Nodes
6
1 -0.003241555532440543 0.002343569416552782 0.005201474763453007
2 -0.00235324539244175 0.003234538016840816 0.005203458480536938
3 -0.003245917148888111 0.00233752466738224 0.006653184536844492
4 -0.002358921570703387 0.00323040084913373 0.006654146127402782
5 -0.003808011068031192 0.001224357518367469 0.005199088249355555
6 -0.003809597576037049 0.001219412544742227 0.006647835951298475
$EndNodes
$Elements
2
1 3 2 2 2 1 2 4 3
2 3 2 2 2 3 6 5 1
$EndElements

The MWE code:

from dolfinx.io import gmshio
from mpi4py import MPI
import meshio
import dolfinx
from dolfinx.fem import VectorFunctionSpace, form, petsc, Function, FunctionSpace
from ufl import Jacobian, as_vector, sqrt, dot, cross, conditional, as_matrix
from ufl import lt,SpatialCoordinate, as_tensor, VectorElement, FiniteElement, MixedElement, Measure
from ufl import  dx, dot, TrialFunction, TestFunction
import petsc4py.PETSc
mesh, subdomains, boundaries = gmshio.read_from_msh("shell_curved_2elem.msh", MPI.COMM_WORLD,0, gdim=3)
Info    : Reading 'shell_curved_2elem.msh'...
Info    : 6 nodes
Info    : 2 elements
Info    : Done reading 'shell_curved_2elem.msh'

#
# Creating local frame using linear shell tutorial 

t = Jacobian(mesh)
t1 = as_vector([t[0, 0], t[1, 0], t[2, 0]])
t2 = as_vector([t[0, 1], t[1, 1], t[2, 1]])
e3 = cross(t1, t2)
e3 /= sqrt(dot(e3, e3))
ey = as_vector([0, 1, 0])
ez = as_vector([0, 0, 1])
e1 = cross(ey, e3)
norm_e1 = sqrt(dot(e1, e1))
e1 = conditional(lt(norm_e1, 0.5), ez, e1 / norm_e1)
e2 = cross(e3, e1)
norm_e2 = sqrt(dot(e2, e2))
e2 /= sqrt(dot(e2, e2))

# Creating ufl form
e=[e1,e2,e3]

def eps(v):
    # taking derivative of v in local frame (local tangent gradient)
    # dot(e1,as_vector([v[0].dx(i) for i in range(3)])) --> d(v[0])/d(e1)
    
    ww=[dot(e[j],as_vector([v[0].dx(i) for i in range(3)])) for j in range(3)]
    [w11,w12,w13]=ww
    r21= dot(e2,as_vector(ww))
    
    vv=e2[0]*r21
    return vv

# creating form
V = VectorFunctionSpace(mesh, ("S", 1))
dv = TrialFunction(V)
v_ = TestFunction(V)

F=(eps(dv)*eps(v_))*dx
form(F)
---------------------------------------------------------------------------
MemoryError                               Traceback (most recent call last)
Cell In[5], line 39
     36 v_ = TestFunction(V)
     38 F=(eps(dv)*eps(v_))*dx
---> 39 form(F)

File /usr/lib/petsc/lib/python3/dist-packages/dolfinx/fem/forms.py:188, in form(form, dtype, form_compiler_options, jit_options)
    185         return list(map(lambda sub_form: _create_form(sub_form), form))
    186     return form
--> 188 return _create_form(form)

File /usr/lib/petsc/lib/python3/dist-packages/dolfinx/fem/forms.py:183, in form.<locals>._create_form(form)
    180 """Recursively convert ufl.Forms to dolfinx.fem.Form, otherwise
    181 return form argument"""
    182 if isinstance(form, ufl.Form):
--> 183     return _form(form)
    184 elif isinstance(form, collections.abc.Iterable):
    185     return list(map(lambda sub_form: _create_form(sub_form), form))

File /usr/lib/petsc/lib/python3/dist-packages/dolfinx/fem/forms.py:141, in form.<locals>._form(form)
    139 if mesh is None:
    140     raise RuntimeError("Expecting to find a Mesh in the form.")
--> 141 ufcx_form, module, code = jit.ffcx_jit(mesh.comm, form,
    142                                        form_compiler_options=form_compiler_options,
    143                                        jit_options=jit_options)
    145 # For each argument in form extract its function space
    146 V = [arg.ufl_function_space()._cpp_object for arg in form.arguments()]

File /usr/lib/petsc/lib/python3/dist-packages/dolfinx/jit.py:56, in mpi_jit_decorator.<locals>.mpi_jit(comm, *args, **kwargs)
     51 @functools.wraps(local_jit)
     52 def mpi_jit(comm, *args, **kwargs):
     53 
     54     # Just call JIT compiler when running in serial
     55     if comm.size == 1:
---> 56         return local_jit(*args, **kwargs)
     58     # Default status (0 == ok, 1 == fail)
     59     status = 0

File /usr/lib/petsc/lib/python3/dist-packages/dolfinx/jit.py:204, in ffcx_jit(ufl_object, form_compiler_options, jit_options)
    202 # Switch on type and compile, returning cffi object
    203 if isinstance(ufl_object, ufl.Form):
--> 204     r = ffcx.codegeneration.jit.compile_forms([ufl_object], options=p_ffcx, **p_jit)
    205 elif isinstance(ufl_object, ufl.FiniteElementBase):
    206     r = ffcx.codegeneration.jit.compile_elements([ufl_object], options=p_ffcx, **p_jit)

File /usr/lib/python3/dist-packages/ffcx/codegeneration/jit.py:199, in compile_forms(forms, options, cache_dir, timeout, cffi_extra_compile_args, cffi_verbose, cffi_debug, cffi_libraries)
    197     except Exception:
    198         pass
--> 199     raise e
    201 obj, module = _load_objects(cache_dir, module_name, form_names)
    202 return obj, module, (decl, impl)

File /usr/lib/python3/dist-packages/ffcx/codegeneration/jit.py:190, in compile_forms(forms, options, cache_dir, timeout, cffi_extra_compile_args, cffi_verbose, cffi_debug, cffi_libraries)
    187     for name in form_names:
    188         decl += form_template.format(name=name)
--> 190     impl = _compile_objects(decl, forms, form_names, module_name, p, cache_dir,
    191                             cffi_extra_compile_args, cffi_verbose, cffi_debug, cffi_libraries)
    192 except Exception as e:
    193     try:
    194         # remove c file so that it will not timeout next time

File /usr/lib/python3/dist-packages/ffcx/codegeneration/jit.py:260, in _compile_objects(decl, ufl_objects, object_names, module_name, options, cache_dir, cffi_extra_compile_args, cffi_verbose, cffi_debug, cffi_libraries)
    256 import ffcx.compiler
    258 # JIT uses module_name as prefix, which is needed to make names of all struct/function
    259 # unique across modules
--> 260 _, code_body = ffcx.compiler.compile_ufl_objects(ufl_objects, prefix=module_name, options=options)
    262 ffibuilder = cffi.FFI()
    263 ffibuilder.set_source(module_name, code_body, include_dirs=[ffcx.codegeneration.get_include_path()],
    264                       extra_compile_args=cffi_extra_compile_args, libraries=cffi_libraries)

File /usr/lib/python3/dist-packages/ffcx/compiler.py:102, in compile_ufl_objects(ufl_objects, object_names, prefix, options, visualise)
    100 # Stage 2: intermediate representation
    101 cpu_time = time()
--> 102 ir = compute_ir(analysis, object_names, prefix, options, visualise)
    103 _print_timing(2, time() - cpu_time)
    105 # Stage 3: code generation

File /usr/lib/python3/dist-packages/ffcx/ir/representation.py:197, in compute_ir(analysis, object_names, prefix, options, visualise)
    191 ir_elements = [_compute_element_ir(e, analysis.element_numbers, finite_element_names)
    192                for e in analysis.unique_elements]
    194 ir_dofmaps = [_compute_dofmap_ir(e, analysis.element_numbers, dofmap_names)
    195               for e in analysis.unique_elements]
--> 197 irs = [_compute_integral_ir(fd, i, analysis.element_numbers, integral_names, finite_element_names,
    198                             options, visualise)
    199        for (i, fd) in enumerate(analysis.form_data)]
    200 ir_integrals = list(itertools.chain(*irs))
    202 ir_forms = [_compute_form_ir(fd, i, prefix, form_names, integral_names, analysis.element_numbers,
    203                              finite_element_names, dofmap_names, object_names)
    204             for (i, fd) in enumerate(analysis.form_data)]

File /usr/lib/python3/dist-packages/ffcx/ir/representation.py:197, in <listcomp>(.0)
    191 ir_elements = [_compute_element_ir(e, analysis.element_numbers, finite_element_names)
    192                for e in analysis.unique_elements]
    194 ir_dofmaps = [_compute_dofmap_ir(e, analysis.element_numbers, dofmap_names)
    195               for e in analysis.unique_elements]
--> 197 irs = [_compute_integral_ir(fd, i, analysis.element_numbers, integral_names, finite_element_names,
    198                             options, visualise)
    199        for (i, fd) in enumerate(analysis.form_data)]
    200 ir_integrals = list(itertools.chain(*irs))
    202 ir_forms = [_compute_form_ir(fd, i, prefix, form_names, integral_names, analysis.element_numbers,
    203                              finite_element_names, dofmap_names, object_names)
    204             for (i, fd) in enumerate(analysis.form_data)]

File /usr/lib/python3/dist-packages/ffcx/ir/representation.py:492, in _compute_integral_ir(form_data, form_index, element_numbers, integral_names, finite_element_names, options, visualise)
    489 integrands = {rule: integral.integrand() for rule, integral in sorted_integrals.items()}
    491 # Build more specific intermediate representation
--> 492 integral_ir = compute_integral_ir(itg_data.domain.ufl_cell(), itg_data.integral_type,
    493                                   ir["entitytype"], integrands, ir["tensor_shape"],
    494                                   options, visualise)
    496 ir.update(integral_ir)
    498 # Fetch name

File /usr/lib/python3/dist-packages/ffcx/ir/integral.py:84, in compute_integral_ir(cell, integral_type, entitytype, integrands, argument_shape, p, visualise)
     74 # Build terminal_data from V here before factorization. Then we
     75 # can use it to derive table properties for all modified
     76 # terminals, and then use that to rebuild the scalar graph more
     77 # efficiently before argument factorization. We can build
     78 # terminal_data again after factorization if that's necessary.
     80 initial_terminals = {i: analyse_modified_terminal(v['expression'])
     81                      for i, v in S.nodes.items()
     82                      if is_modified_terminal(v['expression'])}
---> 84 mt_table_reference = build_optimized_tables(
     85     quadrature_rule,
     86     cell,
     87     integral_type,
     88     entitytype,
     89     initial_terminals.values(),
     90     ir["unique_tables"],
     91     rtol=p["table_rtol"],
     92     atol=p["table_atol"])
     94 # Fetch unique tables for this quadrature rule
     95 table_types = {v.name: v.ttype for v in mt_table_reference.values()}

File /usr/lib/python3/dist-packages/ffcx/ir/elementtables.py:366, in build_optimized_tables(quadrature_rule, cell, integral_type, entitytype, modified_terminals, existing_tables, rtol, atol)
    364 # Clean up table
    365 tbl = clamp_table_small_numbers(t['array'], rtol=rtol, atol=atol)
--> 366 tabletype = analyse_table_type(tbl)
    368 if tabletype in piecewise_ttypes:
    369     # Reduce table to dimension 1 along num_points axis in generated code
    370     tbl = tbl[:, :, :1, :]

File /usr/lib/python3/dist-packages/ffcx/ir/elementtables.py:453, in analyse_table_type(table, rtol, atol)
    450 elif is_ones_table(table, rtol=rtol, atol=atol):
    451     # All values are 1.0
    452     ttype = "ones"
--> 453 elif is_quadrature_table(table, rtol=rtol, atol=atol):
    454     # Identity matrix mapping points to dofs (separately on each entity)
    455     ttype = "quadrature"
    456 else:
    457     # Equal for all points on a given entity

File /usr/lib/python3/dist-packages/ffcx/ir/elementtables.py:420, in is_quadrature_table(table, rtol, atol)
    418 def is_quadrature_table(table, rtol=default_rtol, atol=default_atol):
    419     _, num_entities, num_points, num_dofs = table.shape
--> 420     Id = np.eye(num_points)
    421     return (num_points == num_dofs and all(
    422         np.allclose(table[0, i, :, :], Id, rtol=rtol, atol=atol) for i in range(num_entities)))

File /usr/lib/python3/dist-packages/numpy/lib/twodim_base.py:214, in eye(N, M, k, dtype, order, like)
    212 if M is None:
    213     M = N
--> 214 m = zeros((N, M), dtype=dtype, order=order)
    215 if k >= M:
    216     return m

MemoryError: Unable to allocate 75.2 GiB for an array with shape (100489, 100489) and data type float64

I found that the issue was with using e1, e2 and e3. I stored the values in DG0 function and used DG0 function in weak form. Thereafter, I am not getting the above allocation error. However, I still don’t understand why the previous was not working.

I cannot get the same error message as you got. However, you should reduce the number of quadrature points in your problem. It is estimated to be 626. You can do this through
dx = ufl.Measure("dx", domain=mesh, metadata={"quadrature_degree":15})

mwe:

import ufl
import basix.ufl
from dolfinx.io import gmshio
from mpi4py import MPI
import dolfinx
from dolfinx.fem import form
from ufl import Jacobian, as_vector, sqrt, dot, cross, conditional
from ufl import lt
from ufl import dx, dot, TrialFunction, TestFunction
mesh, subdomains, boundaries = gmshio.read_from_msh(
    "shell_curved_2elem.msh", MPI.COMM_WORLD, 0, gdim=3)

#
# Creating local frame using linear shell tutorial

t = Jacobian(mesh)
t1 = as_vector([t[0, 0], t[1, 0], t[2, 0]])
t2 = as_vector([t[0, 1], t[1, 1], t[2, 1]])
e3 = cross(t1, t2)
e3 /= sqrt(dot(e3, e3))
ey = as_vector([0, 1, 0])
ez = as_vector([0, 0, 1])
e1 = cross(ey, e3)
norm_e1 = sqrt(dot(e1, e1))
e1 = conditional(lt(norm_e1, 0.5), ez, e1 / norm_e1)
e2 = cross(e3, e1)
norm_e2 = sqrt(dot(e2, e2))
e2 /= sqrt(dot(e2, e2))

# Creating ufl form
e = [e1, e2, e3]


def eps(v):
    # taking derivative of v in local frame (local tangent gradient)
    # dot(e1,as_vector([v[0].dx(i) for i in range(3)])) --> d(v[0])/d(e1)

    ww = [dot(e[j], as_vector([v[0].dx(i) for i in range(3)]))
          for j in range(3)]
    [w11, w12, w13] = ww
    r21 = dot(e2, as_vector(ww))

    vv = e2[0]*r21
    return vv


# creating form
V = dolfinx.fem.functionspace(mesh, basix.ufl.element(
    "S", mesh.topology.cell_name(), 1, shape=(3, ), gdim=3))
dv = TrialFunction(V)
v_ = TestFunction(V)

dx = ufl.Measure("dx", domain=mesh, metadata={"quadrature_degree": 15})
F = (eps(dv)*eps(v_))*dx
print(ufl.algorithms.estimate_total_polynomial_degree(
    ufl.algorithms.expand_derivatives(F)))
form(F)

1 Like