Electrostatics Problem with Surface Charge

Hello there,

I am currently struggling with how to implement the variational form for an Electrostatics problem in Fenics, consider a domain \Omega = \Omega_1 \cup \Omega_2 and \Gamma is the boundary between the two (Essentially exactly like the subdomains-poisson demo). And \Gamma_T, \Gamma_B, \Gamma_R and \Gamma_L are the top, bottom, right and left exterior boundaries respectively.

From the subdomains poisson, I can see how to implement the boundary condition:

\varepsilon_1\frac{\partial \varphi_1}{\partial n} = \varepsilon_2\frac{\partial \varphi_2}{\partial n}

Which physically, holds in the absence of surface charge \sigma on the interface.
However, how would the variational form be written if \sigma was taken into account? As in the boundary condition on \Gamma is:

\varepsilon_2\frac{\partial \varphi_2}{\partial n} - \varepsilon_1\frac{\partial \varphi_1}{\partial n} = \sigma

Where \varphi is the electric potential field.

For reference, the whole problem may be stated

\begin{gather} -\nabla^2 \varphi(t) = \frac{\rho+\sigma(t)}{\varepsilon} \ \ \textrm{in} \ \ \Omega \\ \varphi(t) = U_0(t) \ \textrm{on} \ \Gamma_T \\ \varphi(t) = U_1(t) \ \textrm{on} \ \Gamma_B \\ \frac{\partial \varphi(t)}{\partial n} = 0 \ \textrm{on} \ \Gamma_{R,L} \\ \varepsilon_2\frac{\partial \varphi_2(t)}{\partial n} - \varepsilon_1\frac{\partial \varphi_1(t)}{\partial n} = \sigma(t) \ \textrm{on} \ \Gamma \end{gather}

Thanks, :slightly_smiling_face:

Hi @timwong,

I’m not sure if this answer will solve your problem, but hopefully it gives you some useful insight. First, I’m not exactly sure how to interpret your problem statement. You say

but you define \sigma as the surface charge on the interface; therefore it is not defined throughout \Omega. I therefore removed it from the above equation. Since you also seem to allude to a spatially-varying permittivity (i.e. \varepsilon_1 and \varepsilon_2), I re-arranged the problem statement slightly so that it now reads:

-\nabla \cdot (\varepsilon \nabla \varphi(t)) = \rho\, \textrm{in}\, \Omega
\varphi(t) = U_0(t) \, \textrm{on}\, \Gamma_T
\varphi(t) = U_1(t) \, \textrm{on}\, \Gamma_B
\frac{\partial \varphi(t)}{\partial n} = 0\, \textrm{on}\, \Gamma_{R,L}
\varepsilon_2 \frac{\partial \varphi_2(t)}{\partial n} - \varepsilon_1 \frac{\partial \varphi_1(t)}{\partial n} = \sigma(t) \, \textrm{on}\, \Gamma

This ultimately leads to the weak form:
\int_\Omega {\varepsilon \nabla \varphi \cdot \nabla \hat{\varphi} \,dx} = \int_{\Omega}{\rho \hat{\varphi}\,dx} + \int_\Gamma {\sigma \hat{\varphi}\, dS}
where the surface integral over the interface accounts for the surface charge.

This can be implemented as below, for a circle with a surface charge embedded in a unit square. For reasons unknown to me, I had to use superposition of two problems in order to obtain the correct solution. Problem 0 includes the charges (surface and volume) and homogeneous Dirichlet boundaries on the top and bottom, while Problem 1 has no charge contained within the volume and inhomogeneous Dirichlet boundaries U_0 and U_1.

from fenics import *
from mshr import Rectangle, Circle, generate_mesh
import numpy as np

# Create mesh: square with circle in the middle
R = 0.1
domain      = Rectangle(Point(-0.5, -0.5), Point(0.5, 0.5))
inclusion   = Circle(Point(0, 0), R)
domain.set_subdomain(1, inclusion)
mesh        = generate_mesh(domain, 32)

# Define function space
V = FunctionSpace(mesh, "Lagrange", 1)
u = TrialFunction(V)
v = TestFunction(V)

# Dirichlet boundary conditions for top and bottom surfaces. (Neumann
# conditions are applied automatically for left and right surfaces.)
u_0 = Constant(0)
u_T = Constant(0.2)
u_B = Constant(0.1)
def on_top(x, on_boundary):
    return near(x[1], 0.5) and on_boundary
def on_bottom(x, on_boundary):
    return near(x[1], -0.5) and on_boundary
# Homogeneous Dirichlet BCs for problem 0
bc_T_0 = DirichletBC(V, u_0, on_top)
bc_B_0 = DirichletBC(V, u_0, on_bottom)
# Inhomogeneous Dirichlet BCs for problem 1
bc_T_1 = DirichletBC(V, u_T, on_top)
bc_B_1 = DirichletBC(V, u_B, on_bottom)

# Create a MeshFunction on mesh entities of dimension 1 to identify the
# internal boundary. A default value of 0 is assigned to all facets to start.
markers = MeshFunction('size_t', mesh, 1, 0)
# InternalBoundary defines a geometric test to identify facets on the internal
# boundary
class InternalBoundary(SubDomain):
    def inside(self, x, on_boundary):
        return near(np.sqrt(x[0]**2 + x[1]**2),R,0.1*R)
internalBound = InternalBoundary()
# Mark all facets on the internal boundary as belonging to subdomain 1:
internalBound.mark(markers, 1)

# Create an integration measure over the internal boundary. Note the use of
# 'dS' and not 'ds'--lowercase 'ds' integrates only over external facets!
dS = Measure('dS', domain=mesh, subdomain_data=markers)#, subdomain_id=1)

# Define variational problem
eps1    = 1
eps2    = 10
epsilon = Expression('(x[0]*x[0]+x[1]*x[1]<R*R)?eps1:eps2',
                      degree=2,
                      R=R, eps1=eps1, eps2=eps2)
# epsilon = Constant(1)
sigma = Constant(1)  # Surface charge
# rho   = Expression('(x[0]*x[0]+x[1]*x[1]<R*R)?1:0',degree=2,R=R)
rho   = Constant(0)  # Volume charge
a       = dot(epsilon*grad(u), grad(v))*dx
L0      = rho*v*dx + sigma*v("-")*dS(1)
L1      = Constant(0)*v*dx
sol0     = Function(V)
sol0_vec = sol0.vector()
sol1     = Function(V)
sol1_vec = sol1.vector()

# Build system matrix and load vector, removing all constrained dofs
A0, b0 = assemble_system(a, L0, bcs=[bc_T_0, bc_B_0]) 
solve(A0, sol0_vec, b0)
A1, b1 = assemble_system(a, L1, bcs=[bc_T_1, bc_B_1]) 
solve(A1, sol1_vec, b1)

# Project the electric field to a continuous basis and plot
from matplotlib import pyplot as plt
V_E = VectorFunctionSpace(mesh, "Lagrange", 1)
E = project(-epsilon*grad(sol0+sol1), V_E)
plt.figure()
h0 = plot(E)
plt.colorbar(h0)
plt.title("Electric field, $E$")

This produced the following for the electric field:
E_field

2 Likes

Hi @conpierce8 ,

Thank you so much for your answer, it makes a lot of sense, I will try and implement it and see what results I get!

However, I actually asked the same question on the computational science stack exchange here:

And somebody kindly also provided an answer - their formulation appears slightly different to yours though, precisely by a factor of

\frac{1}{\varepsilon_1-\varepsilon_2}

(I accidently swapped the 1 and 2 in that post but point still stands)

on the surface charge integral - Any ideas why this is? (Or if you know which one is correct and why?)

Thanks again,

Hi @timwong,

I think my formulation is correct because it is mine. :grinning:

Joking aside, I do think that my formulation is correct. The Stackexchange answer is very helpful, but I note that the author has added comment:

I think that I’m missing an ε in the surface integrals. I’ll check it later.

I the missing \varepsilon was lost in the derivation of the weak form. Starting from the strong form (and using Einstein summation notation):
-(\varepsilon \varphi_{,i})_{,i} = \rho

Weak form:
-\int_\Omega {(\varepsilon \varphi_{,i})_{,i} \hat{\varphi} \, dx} = \int_\Omega {\rho \hat{\varphi} \,dx}

Product rule of derivatives:
-\int_\Omega {[(\varepsilon \varphi_{,i} \hat{\varphi})_{,i} - \varepsilon \varphi_{,i} \hat{\varphi}_{,i}] \, dx} = \int_\Omega {\rho \hat{\varphi} \,dx}

We can now imagine splitting the integral over \Omega into the sum of two integrals over \Omega_1 and \Omega_2. Applying the divergence theorem we have that the weak form is the sum of the following:
-\int_{\partial \Omega_1} {\varepsilon_1 \varphi_{,i} \hat{\varphi} n_i\, ds} + \int_{\Omega_1} { \varepsilon_1 \varphi_{,i} \hat{\varphi}_{,i} \, dx} = \int_{\Omega_1} {\rho \hat{\varphi} \,dx}
-\int_{\partial \Omega_2} {\varepsilon_2 \varphi_{,i} \hat{\varphi} n_i\, ds} + \int_{\Omega_2} { \varepsilon_2 \varphi_{,i} \hat{\varphi}_{,i} \, dx} = \int_{\Omega_2} {\rho \hat{\varphi} \,dx}

Let \Omega_1 be the circle carrying the surface charge, and \Omega_2 be the surroundings. Then \partial \Omega_1 = \Gamma, and the surface integral over \partial \Omega_2 can be split over the interior (\Gamma) and exterior (\partial \Omega) boundaries. The surface integral over \partial \Omega is 0 (because \varepsilon \varphi_{,i} = 0 where Neumann conditions hold, and \hat{\varphi}=0 where Dirichlet conditions hold), leaving:

-\int_{\Gamma} {\varepsilon_1 \varphi_{,i} \hat{\varphi} n_i\, ds} + \int_{\Omega_1} { \varepsilon_1 \varphi_{,i} \hat{\varphi}_{,i} \, dx} = \int_{\Omega_1} {\rho \hat{\varphi} \,dx}
\int_{\Gamma} {\varepsilon_2 \varphi_{,i} \hat{\varphi} n_i\, ds} + \int_{\Omega_2} { \varepsilon_2 \varphi_{,i} \hat{\varphi}_{,i} \, dx} = \int_{\Omega_2} {\rho \hat{\varphi} \,dx}

where we have introduced a sign change on the integral \int_{\Gamma} {\varepsilon_2 \varphi_{,i} \hat{\varphi} n_i\, ds} so that n_i can unambiguously refer to the outward normal on the interior boundary, as viewed from \Omega_1.

Summing these two equations we find:
-\int_{\Gamma} {(\varepsilon_1 - \varepsilon_2) \varphi_{,i} \hat{\varphi} n_i\, ds} + \int_{\Omega} { \varepsilon \varphi_{,i} \hat{\varphi}_{,i} \, dx} = \int_{\Omega} {\rho \hat{\varphi} \,dx}

Recognizing D_i^{(j)} = -\varepsilon_j \varphi_{,i} we write:
-\int_{\Gamma} {(D^{(2)}_i - D^{(1)}_i) \hat{\varphi} n_i\, ds} + \int_{\Omega} { \varepsilon \varphi_{,i} \hat{\varphi}_{,i} \, dx} = \int_{\Omega} {\rho \hat{\varphi} \,dx}

Or
\int_{\Omega} { \varepsilon \nabla\varphi \cdot \nabla\hat{\varphi} \, dx} = \int_{\Omega} {\rho \hat{\varphi} \,dx} + \int_{\Gamma} {(D^{(2)} - D^{(1)}) \cdot n \hat{\varphi}\, ds}

Finally, making use of the relation (D^{(2)} - D^{(1)})\cdot n = \sigma:
\int_{\Omega} { \varepsilon \nabla\varphi \cdot \nabla\hat{\varphi} \, dx} = \int_{\Omega} {\rho \hat{\varphi} \,dx} + \int_{\Gamma} {\sigma \hat{\varphi}\, ds}

(In this derivation, I think I might be using a different convention than you for the numbering in the jump condition. I am taking n to point from D^{(1)} to D^{(2)}.)

I think the discrepancy occurs in nicoguaro’s answer on Stackexchange when he says

Then, the weak form would be
\int_{\Omega} {\varepsilon \nabla \phi \nabla w \, d\Omega} - \int_{\partial \Omega} {w \nabla \phi \cdot \hat{n} \, d\Gamma} = \int_{\Omega} {f w \, d\Omega}

I believe this should read
\int_{\Omega} {\varepsilon \nabla \phi \nabla w \, d\Omega} - \int_{\partial \Omega} {w \varepsilon \nabla \phi \cdot \hat{n} \, d\Gamma} = \int_{\Omega} {f w \, d\Omega}

Note the additional \varepsilon in the surface integral, which would ultimately eliminate the factor of \frac{1}{\varepsilon_1 - \varepsilon_2}.

2 Likes

@conpierce8

Thank you so much for this detailed derivation - And you’re right I didn’t realise the answer on stack exchange had wrote he had made an error,

Thanks again!