Hi all,
I am trying to assign initial conditions in a circular mesh which I am then using for solving the diffusion equation. What I want to obtain is a constant initial value in the whole mesh except for the most external nodes lying on the external-most circumference, to which I would like to prescribe a specific different value (B_value).
I am using a circular mesh created on Gmsh saved in .msh ASCII version 2 file format ($MeshFormat: 2.2 0 8).
This is a working MWE:
# Initializing parameters
t = 0 # Start time
T = 300 # End time
num_steps = 20 # Number of time steps
dt = (T-t)/num_steps # Time step size
alpha = 3
beta = 1.2
R=1
B_value =10000000
# Diffusion coefficient
D_=0.001
from re import X
import numpy as np
import meshio
import gmsh
import sys
import pygmsh
from dolfinx import mesh, fem
from dolfinx.io import XDMFFile, gmshio
from dolfinx.fem import FunctionSpace, Function, Constant, locate_dofs_geometrical,locate_dofs_topological, dirichletbc, form, Expression
import dolfinx.mesh
import ufl
from mpi4py import MPI
from petsc4py import PETSc
from petsc4py.PETSc import ScalarType
domain, cell_markers, facet_markers = gmshio.read_from_msh("circle.msh", MPI.COMM_WORLD, gdim=2)
V = fem.FunctionSpace(domain, ("CG", 1))
u_n = fem.Function(V)
u_n.interpolate(lambda x: (B_value*np.trunc(np.sqrt(x[0]**2+x[1]**2))))
# BOUNDARY condtion settng Neumann at the center and boarders
boundaries =[(1, lambda x: np.isclose(np.sqrt(x[0]**2+x[1]**2),R)),
(2, lambda x: np.isclose(np.sqrt(x[0]**2+x[1]**2),0))]
facet_indices, facet_markers = [], []
fdim = domain.topology.dim - 1
for (marker, locator) in boundaries:
facets = dolfinx.mesh.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 = dolfinx.mesh.meshtags(domain, fdim, facet_indices[sorted_facets], facet_markers[sorted_facets])
domain.topology.create_connectivity(domain.topology.dim-1, domain.topology.dim)
with XDMFFile(domain.comm, "facet_tags.xdmf", "w") as xdmf:
xdmf.write_mesh(domain)
xdmf.write_meshtags(facet_tag)
ds = ufl.Measure("ds", domain=domain, subdomain_data=facet_tag)
f_=0
f= Constant(domain, ScalarType(f_))
D= Constant(domain, ScalarType(D_))
# creating file to store solution
xdmf = XDMFFile(domain.comm, "2D_Diffusion_Circle.xdmf", "w")
u, v = ufl.TrialFunction(V), ufl.TestFunction(V)
F = u*v*ufl.dx + D*dt*ufl.dot(ufl.grad(u), ufl.grad(v))*ufl.dx - (u_n + dt*f)*v*ufl.dx
# Finishing defining boundaries and apppending to F
class BoundaryCondition():
def __init__(self, type, marker, values):
self._type = type
if type == "Dirichlet":
u_no = Function(V)
u_no.interpolate(values)
facets = facet_tag.find(marker)
dofs = locate_dofs_topological(V, fdim, facets)
self._bc = dirichletbc(u_no, dofs)
elif type == "Neumann":
self._bc = ufl.inner(values, v) * ds(marker)
elif type == "Robin":
self._bc = values[0] * ufl.inner(u-values[1], v)* ds(marker)
else:
raise TypeError("Unknown boundary condition: {0:s}".format(type))
@property
def bc(self):
return self._bc
@property
def type(self):
return self._type
# Define the Dirichlet condition
boundary_conditions = [BoundaryCondition("Neumann", 1, 0),
BoundaryCondition("Neumann", 2, 0)
]
bcs = []
for condition in boundary_conditions:
if condition.type == "Dirichlet":
bcs.append(condition.bc)
else:
F += condition.bc
a = fem.form(ufl.lhs(F))
L = fem.form(ufl.rhs(F))
A = fem.petsc.assemble_matrix(a)
A.assemble()
b = fem.petsc.create_vector(L)
uh = fem.Function(V)
solver = PETSc.KSP().create(domain.comm)
solver.setOperators(A)
solver.setType(PETSc.KSP.Type.PREONLY)
solver.getPC().setType(PETSc.PC.Type.LU)
# write value of initial condition
xdmf.write_mesh(domain)
xdmf.write_function(u_n, t)
for n in range(num_steps):
# Update Diriclet boundary condition
# u_exact.t+=dt
# u_D.interpolate(u_exact)
# Update the right hand side reusing the initial vector
with b.localForm() as loc_b:
loc_b.set(0)
fem.petsc.assemble_vector(b, L)
# Apply Dirichlet boundary condition to the vector
# fem.petsc.apply_lifting(b, [a], [[bcs]])
# b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
# fem.petsc.set_bc(b, [bcs])
# Solve linear problem
solver.solve(b, uh.vector)
uh.x.scatter_forward()
# Update solution at previous time step (u_n)
u_n.x.array[:] = uh.x.array
# write value of computed solution at each step
t += dt
xdmf.write_function(u_n, t)
xdmf.close()
The way I am defining the initial condition interpolating the lambda function is the only way I was able to run the code, but it is not optimal (it assigns the B_value only to half of the boundary) and I would like a more elegant way.
I have tried some other ways to assign initial conditions, all based on the tutorials and some other examples posted here on the forum, with no success. I will briefly list them:
(1)
class initial_condition():
def __init__(self, B_value, R):
self.B_value = B_value
self.R = R
def __call__(self, x):
if np.isclose(x[0]**2 + x[1]**2, self.R*self.R):
return self.B_value
else:
return 0
u_init = initial_condition(B_value, R)
u_n = fem.Function(V)
u_n.interpolate(u_init)
error: File â/root/shared/2D_Diffusion_Circle.pyâ, line 162, in call
if np.isclose(x[0]**2 + x[1]**2, self.R*self.R):
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
(2)
x= ufl.SpatialCoordinate(domain)
def initial_condition(x):
if ufl.exp(np.isclose(np.sqrt(x[0]**2 + x[1]**2), 1)):
return B_value
else:
return 0
expr=fem.Expression(initial_condition(x),V.element.interpolation_points())
u_n = fem.Function(V)
u_n.interpolate(expr)
Error: AttributeError: âSumâ object has no attribute âsqrtâ
(3)
def inside(x):
return (x[0]*x[0] + x[1]*x[1] < R*R)
def boundary(x):
return np.isclose(x[0]*x[0] + x[1]*x[1],R*R)
cells_inside = dolfinx.mesh.locate_entities(domain, domain.topology.dim, inside)
cells_boundary = dolfinx.mesh.locate_entities(domain, domain.topology.dim, boundary)
initial=fem.Function(V)
initial.x.array[cells_inside] = np.full_like(cells_inside, 0, dtype=ScalarType)
initial.x.array[cells_boundary] = np.full_like(cells_boundary, B_value, dtype=ScalarType)
u_n = fem.Function(V)
u_n.interpolate(initial)
Error: File â/root/shared/2D_Diffusion_Circle.pyâ, line 166, in
initial.x.array[cells_inside] = np.full_like(cells_inside, 0, dtype=ScalarType)
IndexError: index 455237 is out of bounds for axis 0 with size 455237
I guess all the error messages somehow depend on the definition of points lying on the external circuference (x^2+y^2==R) and on the implicit definition of x.
Could anyone spot what I am doing wrong or please suggest in which direction to go?
Thank you!
Marta