I would like to convert a working legacy FEniCS code to FEniCSx. It’s a mixed elements problem, where I solve div(J) = 0 and the heat equation (Fourier + Joule heat terms = 0) at the same time. It is a time-independent problem. Therefore, the solutions I seek are the voltage and the temperature.
I am almost there, but I am stuck at initializing values for the voltage and temperature (otherwise Newton’s method fail to converge). Here is my full code:
Sorry, I cannot share the file.msh, too many characters for discourse website.
The mesh is generated with Gmsh, the file.geo is:
SetFactory("OpenCASCADE");
Rectangle(1) = {0, 0, 0, 1, 2, 0};
Rectangle(2) = {-1, 0, 0, 1, 2, 0};
Point(11) = {-1, 1.4, 0, 1.0};
Point(12) = {-1, 1.1, 0, 1.0};
Point(13) = {1, 0.1, 0, 1.0};
Point(14) = {1, 0.2, 0, 1.0};
Line(20) = {11, 12};
Line(21) = {14, 13};
BooleanFragments{ Surface{1}; Delete; }{ Surface{2}; Delete; }
BooleanFragments{ Curve{20}; Delete; }{Surface{2}; Delete;}
BooleanFragments{ Curve{21}; Delete; }{Surface{1}; Delete;}
Physical Surface("material A", 11) = {2};
Physical Surface("material B", 12) = {1};
Physical Curve("left_mat_A", 13) = {20};
Physical Curve("right_mat_B", 14) = {21};
My FEniCSx code shown below contains some doubts I have, as comments.
from mpi4py import MPI
import gmsh
from dolfinx.io import gmshio
mesh, cell_markers, facet_markers = gmshio.read_from_msh("file.msh", MPI.COMM_WORLD, gdim=2)
proc = MPI.COMM_WORLD.rank
import meshio
def create_mesh(mesh, cell_type, prune_z=True):
cells = mesh.get_cells_type(cell_type)
cell_data = mesh.get_cell_data("gmsh:physical", cell_type)
points = mesh.points[:,:2] if prune_z else mesh.points
out_mesh = meshio.Mesh(points=points, cells={cell_type: cells}, cell_data={"name_to_read":[cell_data]})
return out_mesh
if proc == 0:
# Read in mesh
msh = meshio.read("meshes/rectangles_smaller_contacts.msh")
# Create and save one file for the mesh, and one file for the facets
triangle_mesh = create_mesh(msh, "triangle", prune_z=True)
line_mesh = create_mesh(msh, "line", prune_z=True)
meshio.write("meshes/mesh_smaller_contacts.xdmf", triangle_mesh)
meshio.write("meshes/mt_smaller_contacts.xdmf", line_mesh)
# Now read the mesh files produced above.
from dolfinx.io import XDMFFile
with XDMFFile(MPI.COMM_WORLD, "meshes/mesh_smaller_contacts.xdmf", "r") as xdmf:
mesh = xdmf.read_mesh(name="Grid")
ct = xdmf.read_meshtags(mesh, name="Grid")
mesh.topology.create_connectivity(mesh.topology.dim, mesh.topology.dim-1)
with XDMFFile(MPI.COMM_WORLD, "meshes/mt_smaller_contacts.xdmf", "r") as xdmf:
ft = xdmf.read_meshtags(mesh, name="Grid")
from dolfinx.fem import (Constant, dirichletbc, Function, FunctionSpace, assemble_scalar,
form, locate_dofs_geometrical, locate_dofs_topological)
from ufl import (SpatialCoordinate, TestFunction, TrialFunction,
dx, grad, inner, as_tensor, inv, MixedElement, FiniteElement, split)
import numpy as np
# Define ME function space
el = FiniteElement("CG", mesh.ufl_cell(), 1)
mel = MixedElement([el, el])
ME = FunctionSpace(mesh, mel)
u, v = TestFunction(ME)
TempVolt = Function(ME)
temp, volt = split(TempVolt)
rho = 1e-9
sigma = 1 / rho
kappa = 144
# Reminder of the mesh:
# Physical Surface("material A", 11) = {2};
# Physical Surface("material B", 12) = {1};
# Physical Curve("left_mat_A", 13) = {8};
# Physical Curve("right_mat_B", 14) = {2};
inj_current_curve = 13
vanish_voltage_curve = 14 # This corresponds to the curve the current leaves the material.
V_current_contact_out = 0.0 # Voltage value of the curve where the current leaves the material.
reading_voltage_surface_0 = 13
reading_voltage_surface_1 = 14
# We define the boundary conditions
from petsc4py.PETSc import ScalarType
left_facets = ft.find(inj_current_curve)
left_dofs = locate_dofs_topological(ME.sub(1), mesh.topology.dim-1, left_facets)
bcs = [dirichletbc(ScalarType(V_current_contact_out), left_dofs, ME.sub(1))]
from dolfinx.fem import assemble_scalar, form
from ufl import Measure
x = SpatialCoordinate(mesh) # Doubt here. Do I need this? And is it really "mesh"? proc = 0.
print(x)
#print(x.shape)
dx = Measure("dx", domain=mesh,subdomain_data=ct)
ds = Measure("ds", domain=mesh, subdomain_data=ft)
the_current = 2.0 # Current, in amperes.
J = the_current / assemble_scalar(form(1 * ds(inj_current_curve, domain=mesh)))
print(J)
print('Length of curve where current is injected', assemble_scalar(form(1 * ds(inj_current_curve, domain=mesh))))
print('Length of curve where current leaves the wire', assemble_scalar(form(1 * ds(vanish_voltage_curve, domain=mesh))))
from ufl import dot
def temp_init(x):
values = np.zeros((300.0, x.shape))
return values
def volt_init(x):
values = np.zeros((1.0e-3, x.shape))
return values
# Weak form.
TempVolt.sub(0).interpolate(temp_init)
TempVolt.sub(1).interpolate(volt_init)
J_vector = -sigma * grad(volt)
Fourier_term = dot(kappa * grad(temp), grad(u)) * dx
Joule_term = dot(rho * J_vector, J_vector) * u * dx
F_T = Fourier_term + Joule_term
F_V = dot(grad(v), sigma * grad(volt))*dx + v * J * ds(vanish_voltage_curve)
weak_form = F_T + F_V
From what I have read at How to define initial conditions in mixed formulation using dolfinx? - #3 by domi and dolfinx/demo_mixed-poisson.py at d376b20b4b7bf5b85bd3a26582ec9683864bd23e · FEniCS/dolfinx · GitHub, I need to interpolate initial values, by using “x” which is a spatial coordinate of the mesh. I am not sure I have implemented this part correctly in my code.
The error returned is:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
File /usr/local/dolfinx-real/lib/python3.10/dist-packages/dolfinx/fem/function.py:343, in Function.interpolate(self, u, cells)
341 try:
342 # u is a Function or Expression (or pointer to one)
--> 343 _interpolate(u, cells)
344 except TypeError:
345 # u is callable
File /usr/lib/python3.10/functools.py:889, in singledispatch.<locals>.wrapper(*args, **kw)
886 raise TypeError(f'{funcname} requires at least '
887 '1 positional argument')
--> 889 return dispatch(args[0].__class__)(*args, **kw)
File /usr/local/dolfinx-real/lib/python3.10/dist-packages/dolfinx/fem/function.py:319, in Function.interpolate.<locals>._interpolate(u, cells)
318 """Interpolate a cpp.fem.Function"""
--> 319 self._cpp_object.interpolate(u, cells)
TypeError: interpolate(): incompatible function arguments. The following argument types are supported:
1. (self: dolfinx.cpp.fem.Function_float64, f: numpy.ndarray[numpy.float64], cells: numpy.ndarray[numpy.int32]) -> None
2. (self: dolfinx.cpp.fem.Function_float64, u: dolfinx.cpp.fem.Function_float64, cells: numpy.ndarray[numpy.int32]) -> None
3. (self: dolfinx.cpp.fem.Function_float64, expr: dolfinx::fem::Expression<double>, cells: numpy.ndarray[numpy.int32]) -> None
Invoked with: <dolfinx.cpp.fem.Function_float64 object at 0x7f883236f1f0>, <function temp_init at 0x7f88323b36d0>, array([ 0, 1, 2, ..., 1773, 1774, 1775], dtype=int32)
During handling of the above exception, another exception occurred:
TypeError Traceback (most recent call last)
Cell In [35], line 12
8 return values
10 # Weak form.
---> 12 TempVolt.sub(0).interpolate(temp_init)
13 TempVolt.sub(1).interpolate(volt_init)
15 #TempVolt_init=Expression(('300.0','1.0e-3'),degree=1)
16 #TempVolt.interpolate(TsteadyVsteady_init)
File /usr/local/dolfinx-real/lib/python3.10/dist-packages/dolfinx/fem/function.py:348, in Function.interpolate(self, u, cells)
346 assert callable(u)
347 x = _cpp.fem.interpolation_coords(self._V.element, self._V.mesh, cells)
--> 348 self.interpolate(u(x), cells)
Cell In [35], line 3, in temp_init(x)
2 def temp_init(x):
----> 3 values = np.zeros((300, x.shape))
4 return values
TypeError: 'tuple' object cannot be interpreted as an integer