This is a read only copy of the old FEniCS QA forum. Please visit the new QA forum to ask questions

Neumann BC depending on solution

0 votes

Hello,

I have a domain containing two subdoimains and want to apply a Neumann BC on the border between them.

The function defining the Neumann BC depends on the values of the Solution in both subdomains (or more precisely on their difference).

Is it possible to get the values of a cell which is not the actual visited one?

My UFL-File shows as

cell = triangle

scalar = FiniteElement("Lagrange", cell, 1)

phi = TrialFunction(scalar)
v = TestFunction(scalar)
f = Coefficient(scalar)
g = Coefficient(scalar)
sigma = Coefficient(scalar)

a = -sigma*inner(grad(phi), grad(v))*dx - phi*inner(grad(sigma), grad(v))*dx

L = f*v*dx + g*v*ds

where g is the function defining the Neumann BC.

I need to define g in a way that I can access the data of other cells (the ones directly on the left and on the right of the border).

Therefore I tried to derive g from dolfin::Expression but I actually do not know how to access the solution-data of the cells.
Any idea?

Thanks a lot!

Justus

asked Aug 18, 2016 by Pama328 FEniCS Novice (480 points)

If I've understood correctly, you want your solution to have a discontinuity across the interface between subdomains? It's not clear from your question how you will allow this discontinuity, considering your code indicates the use of continuous Lagrange elements. Do you have disjoint meshes for the subdomains?

No I do not want to have discontinuity in the solution. The solution should be a continuous function for both subdomains which has a to fulfill a condition at the boundary between the subdomains, defined by the Neumann BC.

This condition is a value depending on the solution in the first subdomain near the boundary and on the solution in the second one near the boundary (evaluated at x[0]-DOLFIN_EPS for the first subdomain and x[0]+DOLFIN_EPS for the second subdomain, assuming that x is the current position the subdomain-value should be calculated for)

If the solution is continuous, won't the values be the same on both sides of the boundary?

Sorry, I should have stated that in each subdomain sigma has an unique value and as seen in the UFL File the solution depends on this sigma. This should cause the solution not to be the same on both subdomains

I thought a lot about it yesterday and now I'm thinking a discontinuous solution would be even better. Which Functionspace would you suggest for this and do you have an idea how to implement the Neumann BC?

1 Answer

+3 votes

To make the discussion a bit more concrete, consider the problem

$$ \begin{alignat}{2} - \nabla \cdot (a \nabla u) &= f &&\mbox{in }\Omega \\ u &= 0 &&\mbox{on }\partial\Omega \\ a \nabla u \cdot n &= k[\![u]\!] \quad &&\mbox{on }\Gamma, \end{alignat} $$

where $\Gamma$ is a curve dividing $\Omega$ into two subdomains $\Omega_1$ and $\Omega_2$, $n$ is the unit normal vector on $\Gamma$ pointing into $\Omega_2$, $[\![u]\!]$ is the jump in $u$ going from $\Omega_1$ to $\Omega_2$, and $k\geq 0$.

The variational formulation of the problem in the space $V = H^1_{0,\partial\Omega}(\Omega_1)+H^1_{0,\partial\Omega}(\Omega_2)$ reads

$$ \int_\Omega a \nabla u \cdot v\, dx + \int_\Gamma k [\![u]\!] [\![v]\!]\, ds = \int_\Omega f v \, dx$$

for all $v\in V$. The bilinear form is bounded, symmetric and positive definite for reasonable $a$ and $k$, so the problem is well-posed in $V$.

The ideal way to obtain a numerical solution to the problem would be to define Lagrange finite element spaces $V_{h,1}\subset H^1_{0,\partial\Omega_1}$ and $V_{h,1}\subset H^1_{0,\partial\Omega_2}$, and write down the variation problem on $V_h = V_{h,1} + V_{h,2} \subset V$ and solve. Unfortunately, this is not yet possible in FEniCS. But there are several ways to get around this limitation, at cost of having to solve a larger linear system.

One way is to adopt a discontinuous Galerkin formulation, as in the DG-Poisson demo, replacing the standard DG terms on the facets that are on $\Gamma$ with the the term $\int k[\![u]\!][\![v]\!]\, ds$.

A closely related method is to solve a mixed form of the Poisson problem, using discontinuous elements for the scalar field, as in the mixed-Poisson demo.

A third way to solve the problem is to let $V_{h,1}$ and $V_{h,2}$ be defined everywhere and implement $V_h$ as a mixed space $V_{h,1}\times V_{h,2}$. This leads to an excess number of dofs that have to be eliminated after assembling the bilinear form. See code example below.

from dolfin import *
from matplotlib import pyplot
parameters["plotting_backend"] = "matplotlib"

N = 32
mesh = UnitSquareMesh(N, N)
x = SpatialCoordinate(mesh)

# indicator functions for the two subdomains
I0 = conditional(x[0] < 0.5, 1, 0)
I1 = conditional(x[0] > 0.5, 1, 0)

# material parameters and source term
c0, c1 = Constant(1.0), Constant(1.0)
c = I0 * c0 + I1 * c1
k = Constant(8.0)


# define exact solution and source term
U0 = 4*x[0]**2
U1 = (1 + 4 * (c0/k)) * 4 * x[0] * (1 - x[0]) \
   + 4 * (c0/c1) * (2*x[0] - 1) * (1 - x[0])
U = (I0 * U0 + I1 * U1) * sin(pi * x[1])

f = - c * div(grad(U))

# define subdomains and cell measure
cell_domains = CellFunction("size_t", mesh)
right = CompiledSubDomain("x[0]>0.5-DOLFIN_EPS")
right.mark(cell_domains, 1)
dx = Measure("dx", domain = mesh, subdomain_data = cell_domains)

# define interface and facet measure
facet_domains = FacetFunction("size_t", mesh)
interface = CompiledSubDomain("fabs(x[0]-0.5)<DOLFIN_EPS")
interface.mark(facet_domains, 1)
dS = Measure("dS", domain = mesh, subdomain_data = facet_domains) 

# define variational forms
W = VectorFunctionSpace(mesh, "CG", 1)
u, v = TrialFunction(W), TestFunction(W)

# dirichlet boundary on the exterior
bc = DirichletBC(W, (0., 0.), "on_boundary")

# cell integrals
a = c0 * inner(grad(u[0]), grad(v[0])) * dx(0) \
  + c1 * inner(grad(u[1]), grad(v[1])) * dx(1) 

L = f * v[0] * dx(0) + f * v[1] * dx(1)

# interface integral
jmp_u = avg(u[0]) - avg(u[1])
jmp_v = avg(v[0]) - avg(v[1])

a  += k * jmp_u * jmp_v * dS(1)

# assemble and eliminate excess dofs
A, b = assemble_system(a, L, bc)
A.ident_zeros()

# solve and compute L^2-error
w = Function(W)
solve(A, w.vector(), b)
L2_error = assemble((U-w[0])**2 * dx(0) + (U-w[1])**2 * dx(1))**.5

print "||u - uh; L^2|| = {0:1.3e}".format(L2_error)

# project to DG0 and plot
V = FunctionSpace(mesh, "DG", 0)
u, v = TrialFunction(V), TestFunction(V)
u_dg = Function(V)
solve(u*v * dx == v*w[0]*dx(0) + v*w[1]*dx(1), u_dg)
plot(u_dg); pyplot.show()
answered Aug 27, 2016 by Magne Nordaas FEniCS Expert (13,820 points)
edited Aug 27, 2016 by Magne Nordaas

Thanks a lot, the third method is exactly what I have been looking for.

For now my problem is to seperate your code example in a .cpp/.h and a .ufl-File because my whole project is cpp-based.

At the moment I'm stuck at the part

# define subdomains and cell measure
cell_domains = CellFunction("size_t", mesh)
right = CompiledSubDomain("x[0]>0.5-DOLFIN_EPS")
right.mark(cell_domains, 1)
dx = Measure("dx", domain = mesh, subdomain_data = cell_domains)

# define interface and facet measure
facet_domains = FacetFunction("size_t", mesh)
interface = CompiledSubDomain("fabs(x[0]-0.5)<DOLFIN_EPS")
interface.mark(facet_domains, 1)
dS = Measure("dS", domain = mesh, subdomain_data = facet_domains) 

I guess it is a mix between "regular Python expressions" and "UFL-Expression" because there are several functions which are only available in one of both parts.

Thile trying to spereate these parts I was wondering how to represent the CompiledSubDomain class (which is only available in the Python-Interface for FEniCS and not for cpp).

Is it possible two have dynamic boundaries for the Subdomains (for example by defining them like this:

#Subdomain Beginnings and endings for Dynamic Subdomains
D1_x_b = Constant(continousScalar)
D1_x_e = Constant(continousScalar)
D1_y_b = Constant(continousScalar)
D1_y_e = Constant(continousScalar)

D2_x_b = Constant(continousScalar)
D2_x_e = Constant(continousScalar)
D2_y_b = Constant(continousScalar)
D2_y_e = Constant(continousScalar)

#indicator functions for subdomains
I1 = conditional(x[0]>D1_x_b and x[0]<D1_x_e and x[1]>D1_y_b and x[1]<D1_y_e, 1, 0)
I2 = conditional(x[0]>D2_x_b and x[0]<D2_x_e and x[1]>D2_y_b and x[1]<D2_y_e, 1, 0)
I0 = conditional(not(I1 or I2), 1, 0)

or should these values be hard-coded as in your example?(mine is not compiling yet))

If it is possible to set these values dynamicly, how would I do the same in

right = CompiledSubDomain("x[0]>0.5-DOLFIN_EPS")

? Or to be more precicesly: How should the string look in that case?

See the subdomains demo for how to define subdomains in C++. The Python CompiledSubDomain class is shorthand for compiling a C++ subdomain imlementation.

Also take a look at the DG Poisson demo, it shows how to define restricted measures and attach them to variational forms.

...