Problem with assemble_scalar in thermoelastic problem

Hello,

I’m having problems evaluating an integral with Dolfinx and I wonder if anyone could give me some tips. The integral is

image

in which u and T are the displacement and temperature of a body, obtained as the solution of a coupled thermoelastic problem. As a sanity check, I’m considering the case in which T = 0, so that the second and third term on the integral are expected to be zero. When I use assemble_scalar to evaluate each term separately, I get the expected result. However, when I assemble the whole expression, I get a different result. My question is why is this occurring.

I should also mention that I have two subdomains, which are identified from a level set function with UFL conditional, as discussed here. This is why the integral on D is obtained as the sum of the integral in each subdomain.

Here is the code which reproduces the error. It seems that the problem is in the last term, since when I replace T^2 with T, the error disappears.

Thanks in advance!

from dolfinx import fem
from ufl import FacetNormal, Measure, conditional, le, gt, dx, Identity, nabla_grad, div, inner, grad, TrialFunction, TestFunction, VectorElement, MixedElement, FiniteElement, split
from mpi4py import MPI
from dolfinx.mesh import (create_rectangle, CellType, locate_entities, meshtags)
import dolfinx.cpp as _cpp
from petsc4py.PETSc import ScalarType
import numpy as np

def tag_boundaries(msh, subdomains, dim):
    indices, markers = [], [] 
    for (marker, locator) in subdomains:
        facets = locate_entities(msh, dim, locator)
        indices.append(facets)
        markers.append(np.full_like(facets, marker))
    indices = np.hstack(indices).astype(np.int32)
    markers = np.hstack(markers).astype(np.int32)
    sorted_indices = np.argsort(indices)
    return meshtags(msh, dim, indices[sorted_indices], markers[sorted_indices]) 

def apply_boundary_conditions(msh, bc_list):
    fdim = msh.topology.dim - 1 
    bcs = []
    for (facets, g, subspace, collapsed_subspace) in bc_list:
        w_bc = fem.Function(collapsed_subspace)
        w_bc.interpolate(lambda x: g(x))
        bc_dofs = fem.locate_dofs_topological((subspace, collapsed_subspace), fdim, facets)
        bcs.append(fem.dirichletbc(w_bc, bc_dofs, subspace))
    return bcs    

#----------------  Material properties  ----------------------------------
E = 199.5E9; nu = 0.3; 
mu, lbd= E/(2.0*(1.0 + nu)), E*nu/((1.0 + nu)*(1.0 - 2.0*nu))
alpha =   15.4e-6; kappa = 90; beta = alpha*E/(1-2*nu) 
eps = 1e-3

def epsilon(u):
    return 0.5*(nabla_grad(u) + nabla_grad(u).T)

def sigma(u):
    return 2*mu*epsilon(u) + lbd*div(u)*Identity(cdim) 

#------------------ Domain geometry -----------------------------------------------------
radius = 0.25; tol =  1e-8
msh = create_rectangle(comm=MPI.COMM_WORLD,
                    points=((0.0, 0.0), (1.0, 1.0)), n=(200, 100),
                    cell_type=CellType.triangle, diagonal = _cpp.mesh.DiagonalType.crossed)
msh.topology.create_connectivity(msh.topology.dim - 1, msh.topology.dim)
comm = MPI.COMM_WORLD
cdim, fdim = msh.topology.dim, msh.topology.dim - 1 
dx = Measure("dx", domain = msh); n = FacetNormal(msh) 

#----------------  Material Applied loads  ----------------------------------
Q = fem.Constant(msh, ScalarType( (0.0) ) )
F = fem.Constant(msh, ScalarType( (0.0, -2e7) ) )

#---------------- Function Spaces -------------------------------------------
degree_u = 1; degree_T = 1
el_u = VectorElement("CG", msh.ufl_cell(), degree_u, dim=2) 
el_T = FiniteElement("CG",  msh.ufl_cell(), degree_T)
el_m  = MixedElement([el_u , el_T]) 
W = fem.FunctionSpace(msh, el_m) 
U, _ = W.sub(0).collapse(); V, _ = W.sub(1).collapse() 

# ------ Identifying the two-phases/subdomains from level-set ---------------
def phi(x):
    return np.sqrt((x[0] - 0.5)**2 + (x[1]-0.5)**2) - radius
phi_ = fem.Function(V)
phi_.interpolate(phi)
interior = conditional(le(phi_, tol), 1, 0); exterior = conditional(gt(phi_, tol), 1, 0)

# ------- Dirichlet boundary conditions -------------------------------------
u_dir = lambda x: (np.zeros_like(x[0]), np.zeros_like(x[0]))
T_dir = lambda x: (np.zeros_like(x[0])) 
left_facets = locate_entities(msh, fdim, lambda x: np.isclose(x[0], 0.0))
right_facets = locate_entities(msh, fdim, lambda x: np.isclose(x[0], 1.0))
bc_list = [[np.hstack([left_facets, right_facets]), u_dir, W.sub(0), U],\
            [np.hstack([left_facets, right_facets]), T_dir , W.sub(1), V]]
bcs = apply_boundary_conditions(msh, bc_list)

# ------- Neumann boundary conditions -------------------------------------
def NeumBd(x): return   np.isclose(x[1], 0.0)
boundaries = [(1, lambda x:  NeumBd(x))]
msh.topology.create_connectivity(msh.topology.dim - 1,msh.topology.dim)
facet_tags = tag_boundaries(msh, boundaries, fdim)
ds = Measure("ds", domain=msh, subdomain_data=facet_tags)

# ---- Solving thermoelastic problem -----------------------------------------
TrialF = TrialFunction(W); TestF = TestFunction(W)
(u, T) = split(TrialF); (v, r) = split(TestF)
a_state = inner(sigma(u),epsilon(v)) - beta * T * div(v) + kappa * inner(grad(T), grad(r))
lhs_state = a_state * interior * dx + eps * a_state * exterior * dx
rhs_state = inner(Q,r) * dx + inner(F,v) * ds(1)
petsc_opts={"ksp_type": "preonly", "pc_type": "lu", "pc_factor_mat_solver_type": "mumps"}
problem_state = fem.petsc.LinearProblem(lhs_state, rhs_state, bcs = bcs, petsc_options=petsc_opts)
wh = problem_state.solve()
(uh,Th) = wh.split(); uh = uh; Th = Th

print('\n--------- Assembling each term --------------------\n')
S1 = (1/2)*inner(sigma(uh),epsilon(uh))
term1 = fem.assemble_scalar(fem.form(S1*interior*dx + eps*S1*exterior*dx))
S1 = inner(beta, Th * div(uh))
term2 = fem.assemble_scalar(fem.form(S1*interior*dx + eps*S1*exterior*dx))
S1 = (1/2)* alpha * beta * Th**2 * 3.0
term3 = fem.assemble_scalar(fem.form(S1*interior*dx + eps*S1*exterior*dx))

print('term1                : '+str(term1))
print('term2                : '+str(term2))
print('term3                : '+str(term3)+'\n')

print('term1 - term2        : '+str(term1 - term2))
print('term1 - term2 + term3: '+str(term1 - term2 + term3))

print('\n--------- Assembling the whole expression ----------\n')
S1 = (1/2)*inner(sigma(uh),epsilon(uh)) 
compliance = fem.assemble_scalar(fem.form(S1*interior*dx + eps*S1*exterior*dx))

S1 = (1/2)*inner(sigma(uh),epsilon(uh)) - inner(beta, Th * div(uh)) 
mech_energy = fem.assemble_scalar(fem.form(S1*interior*dx + eps*S1*exterior*dx))

S1 = (1/2)*inner(sigma(uh),epsilon(uh)) - inner(beta, Th * div(uh)) + (1/2)* alpha * beta * Th**2 * 3.0
total_energy = fem.assemble_scalar(fem.form(S1*interior*dx + eps*S1*exterior*dx))

print('term1                : '  +str(compliance))
print('term1 - term2        : '  +str(mech_energy))
print('term1 - term2 + term3: '  +str(total_energy))``
1 Like