import numpy as np
import ufl
import basix
import dolfinx.fem.petsc
from dolfinx.fem import (dirichletbc, Function, FunctionSpace,
locate_dofs_topological)
from dolfinx.fem import Function, FunctionSpace
from dolfinx.fem.petsc import NonlinearProblem
from dolfinx.io import XDMFFile
from dolfinx.mesh import locate_entities, meshtags, locate_entities_boundary
from dolfinx.nls.petsc import NewtonSolver
from ufl import dx, grad, inner, TrialFunction, derivative, Measure, SpatialCoordinate, FacetNormal, dot
from dolfinx import fem, io, mesh
from petsc4py import PETSc
from petsc4py.PETSc import ScalarType
from dolfinx import log, default_scalar_type
from mpi4py import MPI
import dolfinx
print(f"DOLFINx version: {dolfinx.__version__} based on GIT commit: {dolfinx.git_commit_hash} of https://github.com/FEniCS/dolfinx/")
# parameters
xmin = 0 #the coordinate of left boundary
xmax = 1 #the coordinate of right boundary
# Define mesh #nums = [8, 16, 32,64,96, 128,255] number of cells
nx = 20 #100 # Modified on Tue 3 Feb 2026
domain = mesh.create_interval(MPI.COMM_WORLD, nx, [xmin, xmax]) #Added on Mon 2 Feb 2026
x = SpatialCoordinate(domain)
###################################################
#### Parameters : time and load and material ######
###################################################
# Case study : Ceramics
#Non-dimensional parameters
Ko_true = fem.Constant(domain, ScalarType(49.42))
Pn_true = fem.Constant(domain, ScalarType(0.084))
Lu_true = fem.Constant(domain, ScalarType(0.238))
epsilon_true = fem.Constant(domain, ScalarType(0.2))
Bim_true = fem.Constant(domain, ScalarType(3.33))
Bit_true = fem.Constant(domain, ScalarType(2.5))
#Qe = fem.Constant(domain, ScalarType(0.9))
#Le_true = 1/Lu_true
#Modified on Sat 22 Feb 2025
print('Lu_true =', Lu_true, 'Pn_true =', Pn_true, 'epsilontrue =', epsilon_true, 'Ko_true =', Ko_true, 'Bim_true=', Bim_true, 'Bit_true=', Bit_true )
#
###################################################
########## Mixed Space of resolution ##############
###################################################
#
# Define ME function space
# Lagrange" is type of the finite element, and 1(piecewise linear elements) or 2 (piecewise quadratic elements) is the degree
m1 = basix.ufl.element("Lagrange", domain.basix_cell(), 1) # For moisture distribution # Modified on Wed 18 June 2025
T1 = basix.ufl.element("Lagrange", domain.basix_cell(), 1) # For temperature evolution # Modified on Wed 18 June 2025
mixed_el = basix.ufl.mixed_element([m1, T1]) # Mixed element
#Define the function space
ME = fem.functionspace(domain, mixed_el) # Mixed Function space for all dofs
# Create the initial and solution functions of space
u = Function(ME) # current solution # total dofs in the problem/ func for all dofs
print("u dofs:", len(u.x.array))
m_sp = fem.functionspace(domain, m1) # moisture function space
T_sp = fem.functionspace(domain, T1) # temperature function space
Vm, uu_to_m = ME.sub(0).collapse() # dofs in u func subspace
m_n = fem.Function(Vm) # dofs in u func
VT, u_to_T = ME.sub(1).collapse() # dofs in u func subspace
T_n = fem.Function(VT) # dofs in u func
m, T = ufl.split(u) # (trail function) give two subfunctions for two elements/dofs from mixed space. Otherwise decoupled. for nonlinear don't use trial fn
(q, v) = ufl.TestFunctions(ME)
## # Define temporal parameters / Time parametrization
t = 0. # Start time
Tf = 10. # End time
num_steps = 1000 # Number of time steps
dt = ( (Tf-t)/num_steps )
#----------------------Initial Conditions---------------------------#
# Initialize history values
u.x.array[:] = 0.0
m_n.x.array[:] = 0.0
T_n.x.array[:] = 0.0
#dimensionless initial moisture content
def m_init(x):
values = np.zeros((1, x.shape[1]))
values[0] = 0.0
return values
#dimensionless initial temperature content
def T_init(x):
values = np.zeros((1, x.shape[1]))
values[0] = 0.0
return values
m_n.interpolate(m_init)
T_n.interpolate(T_init)
# checking if it is properly imlemented
m_n.name = "moisture" #initial moisture
T_n.name = "temperature" #initial temperature
#----------------------Boundary Conditions---------------------------#
# We start by identifying the facets contained in each boundary and create a
# custom integration measure ds
boundaries = [(1, lambda x: np.isclose(x[0], xmin)), # left (x=0)
(2, lambda x: np.isclose(x[0], xmax))] # right (x=1)
# We now loop through all the boundary conditions and create MeshTags
# identifying the facets for each boundary condition.
facet_indices, facet_markers = [], []
fdim = domain.topology.dim - 1
for (marker, locator) in boundaries:
facets = locate_entities(domain, fdim, locator)
facet_indices.append(facets)
facet_markers.append(np.full_like(facets, marker))
facet_indices = np.hstack(facet_indices).astype(np.int32)
facet_markers = np.hstack(facet_markers).astype(np.int32)
sorted_facets = np.argsort(facet_indices)
facet_tag = meshtags(domain, fdim, facet_indices[sorted_facets], facet_markers[sorted_facets])
# Debugging boundary condition
# To debug boundary conditions, the easiest thing to do is to visualize the boundary in Paraview
# by writing the MeshTags to file. We can then inspect individual boundaries using the Threshold-filter.
domain.topology.create_connectivity(domain.topology.dim-1, domain.topology.dim)
with XDMFFile(domain.comm, "facet_tags_Thu9Apr2026_1D_Backward_Euler_Low_Order_Basis_Functions_ConvectiveBCs.xdmf", "w") as xdmf:
xdmf.write_mesh(domain)
xdmf.write_meshtags(facet_tag, domain.geometry)
# Integration measures
metadata = {"quadrature_degree": 4}
ds = ufl.Measure('ds', domain=domain, subdomain_data=facet_tag, metadata=metadata) #line/area element
dx = ufl.Measure("dx", domain=domain, metadata=metadata) #volume element
Qe = fem.Constant(domain, ScalarType(0.9)) #prescribed heat flux
#n = FacetNormal(domain)
#Q = -dot(n, grad(T))
#print(Q)
F0 = inner(m, q) * dx - inner(m_n, q) * dx + dt * Lu_true * inner(grad(m), grad(q)) * dx - dt * ( Lu_true * Pn_true ) * inner(grad(T), grad(q)) * dx
Term_BCs_m = Lu_true * dt * inner(Bim_true*(m-1), q) * ds(2)
F0_ = F0 + Term_BCs_m
F1 = inner(T, v) * dx - inner(T_n, v) * dx + dt * inner(grad(T), grad(v)) * dx + (epsilon_true * Ko_true) * inner(m, v) * dx - (epsilon_true * Ko_true) * inner(m_n, v) * dx
Term_BCs_T_c = dt * inner(Qe, v) * ds(1)
Term_BCs_T_d = dt * inner(Bit_true*(T-1) - (1-epsilon_true)*Ko_true*Lu_true*Bim_true(m-1), v) * ds(2)
F1_ = F1 + Term_BCs_T_c + Term_BCs_T_d
F = F0_ + F1_
# Non linear problem definition
problem = NonlinearProblem(F, u)
#Solve variational problem
# Create nonlinear problem and Newton solver
solver = NewtonSolver(domain.comm, problem) #solver = NewtonSolver(MPI.COMM_WORLD, problem)
solver.convergence_criterion = "incremental" #residual #incremental
# Absolute tolerance
solver.atol = 5e-10 # 1e-8
solver.rtol = 1e-12 #0.0 #1e-14 #0.0 #1e-12 # 1e-8
#solver.max_it = 70
solver.report = True
#solver.max_it = 100 #To increase the number of iterations
solver.error_on_nonconvergence = False
solver.max_it = 3
#-----------------------------Solution Settings-----------------------------#
Vm_sol, u_to_m_sol = ME.sub(0).collapse() # dofs in u func subspace
m_sol = fem.Function(Vm_sol)
VT_sol, u_to_T_sol = ME.sub(1).collapse() # dofs in u func subspace
T_sol = fem.Function(VT_sol)
#m_sol.name = "moisture"
#T_sol.name = "temperature"
file_moist = dolfinx.io.VTXWriter(domain.comm, "1D_moisture_Backward_Euler_Low_Order_Basis_Functions_Thu9Apr2026_ConvectiveBCs_100mesh.bp", m_n, "bp4")
file_temp = dolfinx.io.VTXWriter(domain.comm, "1D_temperature_Backward_Euler_Low_Order_Basis_Functions_Thu9Apr2026_ConvectiveBCs.bp", T_n, "bp4")
import time
start_time = time.time()
#---------------------------Solve problem-------------------------------------#
duration_solve = 0.0
t = 0.0
file_moist.write(t) # Create file for initial moisture
file_temp.write(t) # Create file for initial temperature
# print
num_steps = int(num_steps)
for n in range(num_steps):
# Update time
t += dt
# Solve newton-steps
log.set_log_level(log.LogLevel.INFO)
duration_solve -= MPI.Wtime()
niter, converged = solver.solve(u)
duration_solve += MPI.Wtime()
PETSc.Sys.Print(
"Phys. Time {:.4f}, Calc. Time {:.4f}, Num. Iter. {}".format(
t, duration_solve, niter
)
)
m_n.x.array[:] = m_sol.x.array
T_n.x.array[:] = T_sol.x.array
m_sl = u.sub(0).collapse()
T_sl = u.sub(1).collapse()
m_sl.x.scatter_forward()
T_sl.x.scatter_forward()
m_sol.interpolate(m_sl)
T_sol.interpolate(T_sl)
m_n.x.array[:] = m_sol.x.array
T_n.x.array[:] = T_sol.x.array
file_moist.write(t)
file_temp.write(t)
file_moist.close()
file_temp.close()
# outfile.close()
end_time = time.time()
print('final time is = ', end_time - start_time)
print(Q)
This is the code I wrote to solve the above system. However, I did not get the expected results. Any help would be much appreciated.