Zero initial conditions

Hi, I am facing a problem with the initial conditions in the heat equation. Basically, when I set the initial condition to be u0(x) = 0 everywhere, the simulation goes wrong and I get weird results, just like if the equation was ignoring the source term and the solution remained 0 everywhere.

import numpy as np
import gmsh
import tqdm

from mpi4py import MPI
from petsc4py import PETSc

from dolfinx import fem, io
from dolfinx.mesh import locate_entities_boundary
from ufl import grad, dx, dot, ds, dS, SpatialCoordinate, TestFunction, TrialFunction, Measure


gdim = 3
mesh_comm = MPI.COMM_WORLD
model_rank = 0
fluid_tag, source_tag = 11, 22

gmsh.initialize()
if mesh_comm.rank == model_rank:
    fluid = gmsh.model.occ.addBox(0, 0, 0, 1, 1, 1)
    source = gmsh.model.occ.addBox(0.4, 0.4, 0.4, 0.2, 0.2, 0.2)
    all_entities = gmsh.model.occ.fragment([(gdim, fluid)], [(gdim, source)])
    gmsh.model.occ.synchronize()
    
    all_vols = gmsh.model.occ.getEntities(gdim)
        
    for dim, tag in all_vols:
        mass = gmsh.model.occ.getMass(dim, tag)
        if np.isclose(mass, 0.2**3):
            source = [tag]
        else:
            fluid = [tag]

    gmsh.model.addPhysicalGroup(gdim, source, source_tag)
    gmsh.model.setPhysicalName(gdim, source_tag, "source")
    
    gmsh.model.addPhysicalGroup(gdim, fluid, fluid_tag)
    gmsh.model.setPhysicalName(gdim, fluid_tag, "fluid")
    
    source_faces = gmsh.model.getBoundary([(gdim, source[0])])
    gmsh.model.mesh.field.add("Distance", 1)
    gmsh.model.mesh.field.setNumbers(1, "FacesList", [e[1] for e in source_faces])
    gmsh.model.mesh.field.add("Threshold", 2)
    gmsh.model.mesh.field.setNumber(2, "IField", 1)
    gmsh.model.mesh.field.setNumber(2, "LcMin", 0.2 / 3)
    gmsh.model.mesh.field.setNumber(2, "LcMax", 3.0 * 0.2)
    gmsh.model.mesh.field.setNumber(2, "DistMin", 2 * 0.2)
    gmsh.model.mesh.field.setNumber(2, "DistMax", 5 * 0.2)
    gmsh.model.mesh.field.add("Min", 5)
    gmsh.model.mesh.field.setNumbers(5, "FieldsList", [2])
    gmsh.model.mesh.field.setAsBackgroundMesh(5)
    
    gmsh.option.setNumber("Mesh.Algorithm", 7)
    gmsh.model.mesh.generate(gdim) 
    gmsh.model.mesh.optimize("Netgen")

mesh, ct, _ = io.gmshio.model_to_mesh(gmsh.model, mesh_comm, 0, gdim=gdim)
gmsh.finalize()

dx = Measure("dx", domain=mesh)
ds = Measure("ds", domain=mesh)

# Espacios de funciones
V, Q = fem.FunctionSpace(mesh, ("CG", 1)), fem.FunctionSpace(mesh, ("DG", 0))

# Fuente
f = fem.Function(Q)
f.x.array[:] = 0.0
source_cells = ct.find(source_tag)
f.x.array[source_cells] = 50.0

# # Condiciones de contorno
#Condiciones de Dirichlet
def boundary_D(x):
    return np.isclose(x[2], 1)


def u_boundary(x):
    return 0.0 * x[0]


dofs_D = fem.locate_dofs_geometrical(V, boundary_D)
u_bc = fem.Function(V)
u_bc.interpolate(u_boundary)
bc = fem.dirichletbc(u_bc, dofs_D)

# Condiciones de Neumann
x = SpatialCoordinate(mesh)
g = 0.0 * x[0]

# Parámetros temporales
t = 0 # Start time
T = 10 # Final time
num_steps = 75 
dt = T / num_steps # time step size
dt = fem.Constant(mesh, PETSc.ScalarType(dt))

# Condición inicial
def initial_condition(x):
    return 0.0 * x[0]

u_n = fem.Function(V)
u_n.interpolate(initial_condition)

uh = fem.Function(V)
uh.interpolate(initial_condition)

# Forma variacional y ensamblar matriz/vector
u, v = TrialFunction(V), TestFunction(V)
a = u * v * dx  +  dt * dot(grad(u), grad(v)) * dx
L = (u_n + dt * f) * v * dx + dt * g * v * ds

bilinear_form = fem.form(a)
linear_form = fem.form(L)

A = fem.petsc.assemble_matrix(bilinear_form, bcs=[bc])
A.assemble()
b = fem.petsc.create_vector(linear_form)

# Solver
solver = PETSc.KSP().create(mesh.comm)
solver.setOperators(A)
solver.setType(PETSc.KSP.Type.PREONLY)
solver.getPC().setType(PETSc.PC.Type.LU)

# Archivo para guardar el output
xdmf = io.XDMFFile(mesh_comm, 'outputs/source.xdmf', 'w')
xdmf.write_mesh(mesh)
xdmf.write_function(uh, t)

timer = tqdm.tqdm(total=num_steps)

for _ in range(num_steps):
    t += dt
    timer.update(1)

    # Updatear la forma lineal
    with b.localForm() as loc_b:
        loc_b.set(0)
    fem.petsc.assemble_vector(b, linear_form)

    # Aplicar condición de dirichlet al vector
    fem.petsc.apply_lifting(b, [bilinear_form], [[bc]])
    b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
    fem.petsc.set_bc(b, [bc])

    # Solve
    solver.solve(b, uh.vector)
    uh.x.scatter_forward()

    # Updatear u_n
    u_n.x.array[:] = uh.x.array

    # Escribir en archivo
    xdmf.write_function(uh, t)

xdmf.close()

However, if I substitute the initial condition in the code above for

def u_boundary(x):
    return 0.1 * x[0]

(or any other function that is not zero everywhere) the equation works fine, and I get the expected behavior: the source radiates heat and the temperature rises according to the boundary conditions.

I am a quite loss with this problem, and any suggestions would be appreciated.
Thank you very much in advance!

I’m not sure what solution you are expecting.
This is what I get after one time step:


12 time steps:

75 time steps:

Your output is exactly what I expect to obtain. Nevertheless, I get something like this in the first step:

And I don’t have any idea of what is happening…
Taking that it worked out correctly for you, I assume that there must be something wrong with my instalation.
Thank you for your quick response.

Start by checking the scaling of your plots in paraview, what is the range?
As your initial condition is 0, paraview probably chooses the 0 to 0 range, Which you have to rescale

Oh God it was the hecking plot range… I can’t belive I’ve been stuck thinking that the simulation was wrong…

Thank you very much, very helpful as always.

PS sometimes I hate computers haha