Dear all,
I am working on the phase field model to generate cracks due to thermal shock. The basic idea is throwing a hot plate into cool water, then we would see the cracks on the hot plate. In the phase model, the thermal conductivity would decrease at the location of cracks. The equation is k = (1 - damage)^2 * k0. damage stands for the crack, when damage = 1, that location is completely a crack.
The problem is that, at the cracks, with the heat conduction keeps going, I will have a few points with temperature far larger than the initial temperature (680 K) gradually. Please see the pictures below. Picture1 is the temperature field of the whole domain. Picture 2 is the crack pattern. Picture 3 is the singularities in one crack branch.
I made a simple code of the crack problem. Initially, I will set a part of the domain as crack with damage = 1 and with a fixed width = n * meshsize. Then, I will do heat conduction in the domain with initial temperature as 680 K, top and right boundaries as insulation, and bottom and left boundaries as 300 K. I found that, if the crack width is small, the maximum temperature (generally at the bottom of the crack) in the domain would be larger than 680 K, and then gradually decrease. If the crack width is large, the maximum temperature would keep going to a very large value. The code is attached as below. Can any one help me with this problem?
import numpy as np
from mpi4py import MPI
import ufl
from ufl import ds, dx, grad, inner, VectorElement, FacetNormal, SpatialCoordinate, TrialFunction, TestFunction, Measure
from dolfinx import mesh, fem, io, plot, default_scalar_type
from dolfinx.fem.petsc import assemble_matrix, assemble_vector, apply_lifting, create_vector, set_bc
import pyvista
from petsc4py import PETSc
initial_dt = 5e-9
conductiontime = 5e-5
t = 0
tol = 1e-7
small = 1e-8
L = 0.025
H = 0.005
nx = 500
ny = 100
meshsize = L / nx
timestep = initial_dt
mesh_comm = MPI.COMM_WORLD
msh = mesh.create_rectangle(MPI.COMM_WORLD, [np.array([0, 0]), np.array([L, H])], [nx, ny], mesh.CellType.triangle)
V = fem.FunctionSpace(msh, ("Lagrange", 1))
WW = fem.FunctionSpace(msh, ("DG", 0))
x = SpatialCoordinate(msh)
T = TrialFunction(V)
v = TestFunction(V)
# set the initial crack part with width = n * meshsize, in the crack, damage = 1, other wise, damage = 0
damage = fem.Function(V)
def damage_interpolation(x):
if x[0] <= 0.01:
return 0
elif x[0] > (0.01 + 4 * meshsize): # set the width of the crack
# if width >= 3 * meshsize, the max temperature will keep increasing
# if width <= 2 * meshsize, the max temperature will be only larger than 680 K at the beginning steps
return 0
else:
return 1
dof_coords = V.tabulate_dof_coordinates()
damage_values = np.array([damage_interpolation(x) for x in dof_coords], dtype=np.float64)
damage.vector.setArray(damage_values)
damage.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)
alpha = fem.Function(WW)
def alpha_PF(p):
return ((1.0 - p)**2 + small) * 300 / 2450 /0.775
alpha_expr = fem.Expression(alpha_PF(damage), WW.element.interpolation_points())
alpha.interpolate(alpha_expr)
alpha.x.scatter_forward()
# alpha = 300 / 2450 /0.775 # if alpha is constant, everything will be all good
def temp_bc(x):
return np.logical_or(np.isclose(x[0], 0), np.isclose(x[1], 0))
dofs_crack = fem.locate_dofs_geometrical(V, temp_bc)
temp_boundary = fem.Function(V)
temp_boundary.interpolate(lambda x: 300.0 + 0 * x[0])
temp_boundary.x.scatter_forward()
bcs = [fem.dirichletbc(temp_boundary, dofs_crack)]
dt = fem.Constant(msh, default_scalar_type(initial_dt))
T_1 = fem.Function(V)
T_1.interpolate(lambda x: 680.0 + 0 * x[0])
T_1.x.scatter_forward()
Th = fem.Function(V)
a = inner(T, v) * dx + dt * inner(alpha * grad(T), grad(v)) * dx
L = inner(T_1, v) * dx
bilinear_form = fem.form(a)
linear_form = fem.form(L)
A = fem.petsc.create_matrix(bilinear_form)
b = fem.petsc.create_vector(linear_form)
solver = PETSc.KSP().create(msh.comm)
solver.setOperators(A)
solver.setType(PETSc.KSP.Type.GMRES)
solver.getPC().setType(PETSc.PC.Type.JACOBI)
solver.setTolerances(rtol=1e-8)
solver.setTolerances(atol=1e-12)
solver.setTolerances(max_it=1000)
while (t<conductiontime):
t += timestep
A.zeroEntries()
fem.petsc.assemble_matrix(A, bilinear_form, bcs=bcs)
A.assemble()
with b.localForm() as loc_b:
loc_b.set(0)
fem.petsc.assemble_vector(b, linear_form)
fem.petsc.apply_lifting(b, [bilinear_form], [bcs])
b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
fem.petsc.set_bc(b, bcs)
solver.solve(b, Th.vector)
Th.x.scatter_forward()
T_1.x.array[:] = Th.x.array
T_1.x.scatter_forward()
max_temp = np.max((Th.x.array))
print('time step: ', round(t, 9), "max temp:", round(max_temp, 3))
if round(t, 9) * 1e8 % 1 == 0:
topo, types, geom = plot.vtk_mesh(V)
grid = pyvista.UnstructuredGrid(topo, types, geom)
grid.point_data["temperature"] = Th.x.array.real
grid.set_active_scalars("temperature")
plotter = pyvista.Plotter()
plotter.add_text("temperature", position="upper_edge", font_size=14, color="black")
plotter.add_mesh(grid, show_edges=False)
plotter.view_xy()
plotter.show()