Set Internal Boundary Condition Using Mixed Dimensional Space

Hi everyone,

I am using docker containr with the image ceciledc/fenics_mixed_dimensional:latest as suggested by user cdaversin for solving a mixed dimensional problem.

I would like to solve a problem consisting of two different Poisson equations on different sinusoidal subdomains coupled by nonlinear boundary condition of Neumann type. Using the mixed_dimensional branch (based on the answer to these 2 questions Coupled Equations Over Multiple Domains , [Internal boundary condition with mixed_dimensional branch] (Internal boundary condition with mixed_dimensional branch), I tried to add the Dirichlet internal boundary condition to the sinusoidal interface, but this gives me the error. I want to set the boundary value = 1 at the top boundary, 0 at the interface and 0 at the bottom boundary.

Once Dirichlet works, I would like to give a non-linear Robin boundary condition like -kappa*gradient(ub) = exp(-ub) at the interface for the top domain and set ua=0 at the interface for the bottom domain.

Error:
Traceback (most recent call last):
File “poisson5.py”, line 135, in
bc_in1 = DirichletBC(W.sub_space(0), a_0, boundary_int, method = “pointwise”) #Bottom domain interface
File “/usr/local/lib/python3.6/dist-packages/fenics_dolfin-2019.2.0.dev0-py3.6-linux-x86_64.egg/dolfin/fem/dirichletbc.py”, line 131, in init
super().init(*args)
TypeError: init(): incompatible constructor arguments. The following argument types are supported:
1. dolfin.cpp.fem.DirichletBC(arg0: dolfin.cpp.fem.DirichletBC)
2. dolfin.cpp.fem.DirichletBC(V: dolfin.cpp.function.FunctionSpace, g: dolfin.cpp.function.GenericFunction, sub_domain: dolfin.cpp.mesh.SubDomain, method: str = ‘topological’, check_midpoint: bool = True)
3. dolfin.cpp.fem.DirichletBC(V: dolfin.cpp.function.FunctionSpace, g: dolfin.cpp.function.GenericFunction, sub_domains: dolfin.cpp.mesh.MeshFunctionSizet, sub_domain: int, method: str = ‘topological’)

Invoked with: <dolfin.cpp.function.FunctionSpace object at 0x7fdcbd78da40>, <dolfin.cpp.function.Constant object at 0x7fdcbd7a1500>, <dolfin.cpp.mesh.MeshFunctionSizet object at 0x7fdcc1954b58>, ‘pointwise’
Thank you!

Code:

        from dolfin import *
        import matplotlib.pyplot as plt
        import numpy as np
        from ufl import nabla_div
        from ufl import sinh
        import random

    # Width of total domain
    width = 100e-6
    # Height of total domain
    height = 200e-6
    # Total resolution
    resolution = 100
    # Forcing function
    f = Constant(0)
    # Boundary values for the two equations
    a_0 = 0		#Bottom boundary Dirichlet
    b_0 = 1		#Top Boundary Dirichlet
    i0 = 12.6
    r = Constant(2*i0)

    iApp = -1


    F = 96485.33
    R = 8.314
    T = 300
    c = Constant(F/(2*R*T))

    kappa = 1
    # g = iApp
    # funcPhi0 = i0*(exp(C1*phi0) - exp(-C1*phi0)) 
    # dFuncPhi0 = i0*C1*(exp(C1*phi0) + exp(-C1*phi0))
    # r = i0*dFuncPhi0
    # s = phi0 - funcPhi0/dFuncPhi0
    g = iApp/kappa

    tol = 1e-14
    # Define SubDomain
    class TopDomain(SubDomain):
        def inside(self, x, on_boundary):
            return x[1] >= 0.0001 + 0.00002*sin(5e4*x[0]) + tol

    # Define Boundary
    class TopBoundary(SubDomain):
        def inside(self, x, on_boundary):
            return on_boundary and near(x[1], height)

    class BottomBoundary(SubDomain):
        def inside(self, x, on_boundary):
            return on_boundary and near(x[1], 0)

    class Interface(SubDomain):
        def inside(self, x, on_boundary):
            return near (x[1], 0.0001 + 0.00002*sin(5e4*x[0]))


    # # Define SubDomain
    # class TopDomain(SubDomain):
    #     def inside(self, x, on_boundary):
    #         return x[1] >= 0.0001 + tol


    # class BottomDomain(SubDomain):
    #     def inside(self, x, on_boundary):
    #         return x[1] <= 0.0001 - tol

    # # Define Boundary
    # class TopBoundary(SubDomain):
    #     def inside(self, x, on_boundary):
    #         return on_boundary and near(x[1], height)

    # class BottomBoundary(SubDomain):
    #     def inside(self, x, on_boundary):
    #         return on_boundary and near(x[1], 0)

    # class Interface(SubDomain):
    #     def inside(self, x, on_boundary):
    #         return near (x[1], 0.0001)

    # Mesh for the entire domain
    mesh_full = RectangleMesh(Point(0, 0),
                              Point(width, height),
                              resolution,
                              resolution)

    # Define domains
    domains = MeshFunction("size_t", mesh_full, 2)
    domains.set_all(0)
    top_domain = TopDomain()
    top_domain.mark(domains, 1)

    # out_sub0 = File("materials.pvd")
    # out_sub0 << domains

    # Create mesh for the left domain
    mesh_top = MeshView.create(domains, 1)
    mesh_bottom = MeshView.create(domains, 0)

    # Define boundaries

    top_boundary = TopBoundary()
    bottom_boundary = BottomBoundary()

    boundary_top = MeshFunction("size_t", mesh_top, 1)
    top_boundary.mark(boundary_top, 1)

    boundary_bottom = MeshFunction("size_t", mesh_bottom, 1)
    bottom_boundary.mark(boundary_bottom, 1) 


    interf = Interface()
    boundary_int = MeshFunction("size_t", mesh_full, 1)
    boundary_int.set_all(0)
    interf.mark(boundary_int, 1)

    mesh_int = MeshView.create(boundary_int, 1)

    # Create the function spaces
    W_a = FunctionSpace(mesh_bottom, "Lagrange", 1)
    W_b = FunctionSpace(mesh_top, "Lagrange", 1)
    W = MixedFunctionSpace(W_a, W_b)

    #dS = Measure("dS", domain=mesh_full, subdomain_data=boundary_int)


    # ds_int = ds_bottom(1)
    ds_int = Measure("dx", domain=mesh_int)
    ds_top = Measure("ds", domain = mesh_top, subdomain_data = boundary_top)
    # ds_bottom = Measure("ds", domain=mesh_bottom,  subdomain_data=boundary_int)

    # Define boundary conditions
    bc_a = DirichletBC(W.sub_space(0), a_0, boundary_bottom, 1)  #Bottom domain
    bc_b = DirichletBC(W.sub_space(1), b_0, boundary_top, 1)	#Top domain
    bc_in1 = DirichletBC(W.sub_space(0), a_0, boundary_int, method = "pointwise") #Bottom domain interface
    bc_in2 = DirichletBC(W.sub_space(1), a_0, boundary_int, method = "pointwise") #Top domain interface

    # bc_c = DirichletBC(W.sub_space(0), a_0, interf, 1) #Interface for Bottom domain
    # bc_d = DirichletBC(W.sub_space(1), a_0, interf, 1) #Interface for Top domain
    bcs = [bc_a, bc_b, bc_in1, bc_in2]	#for Dirichlet condition
    # bcs = [bc_a]			#For Neumann condition at top boundary

    # Create all of the weak form variables
    u = Function(W)
    ua, ub = u.sub(0), u.sub(1)
    va, vb = TestFunctions(W)


    # ub.interpolate(Constant(0.001))
    # ua.interpolate(Constant(0.0))

    # Need to manually specify these.
    dxa = Measure("dx", domain=W.sub_space(0).mesh())
    dxb = Measure("dx", domain=W.sub_space(1).mesh())

    # g = Expression("1.0", degree=2)
    # c = Constant("10.0")

    # The weak formulation
    F_inner = kappa*( inner(grad(ua), grad(va)) * dxa + kappa*inner(grad(ub), grad(vb)) * dxb
                - (f * va * dxa + f * vb * dxb )
                )

    # F_inner = kappa*( inner(grad(ua), grad(va)) * dxa + kappa*inner(grad(ub), grad(vb)) * dxb
    #             - (f * va * dxa + f * vb * dxb ) + g*vb*ds_top + r*sinh(C*ub)*vb*ds_int
    #             )


    # F_inner = kappa*( inner(grad(ua), grad(va)) * dxa + kappa*inner(grad(ub), grad(vb)) * dxb
    #             - (f * va * dxa + f * vb * dxb ) + r*ub*vb*ds_int - r*s*vb*ds_int + g*vb*ds_top
    #             )

    #Neumann boundary condition
    # F_inner = ( inner(grad(ua), grad(va)) * dxa + inner(grad(ub), grad(vb)) * dxb
                # - (f * va * dxa + f * vb * dxb ) + r*ub*vb*ds_int - r*s*vb*ds_int)

    # F_coupling =  va*c*ds_int + vb*c*ds_int
    F_coupling =  0.0

    F = F_inner + F_coupling


    # solve(F==0, u, bcs, solver_parameters= {"nonlinear_solver": "newton",
                   # "newton_solver": {"linear_solver": "gmres"}})

    Fi = extract_blocks(F)

    dFi = []
    for J in Fi:
        for wi in u.split():
            df = derivative(J, wi)
            dFi.append(df)
            
    u_sol = u.split()
    problem = MixedNonlinearVariationalProblem(Fi, u_sol, bcs, dFi)
    solver = MixedNonlinearVariationalSolver(problem)
    solver.solve()

    # J = derivative(F, u)
    # problem = NonlinearVariationalProblem(F, u, bcs, J)
    # solver  = NonlinearVariationalSolver(problem)
    # solver.solve()

    # ## Export last solution
    out_sub0 = File("ua.pvd")
    out_sub1 = File("ub.pvd")
    # out_exact = File("poisson-lm-2D-exact.pvd")
    out_sub0 << u.sub(0)
    out_sub1 << u.sub(1)

To me your error message seems to be due to the fact that you are not sending in the correct arguments to bc_in1. If boundary_int is a meshfunction, you also need to specify which part of the boundary you would like to integrate (1 in your case).

1 Like

Dear Dokken,

Thanks much for the swift response. I added the boundary marker in the following fashion:

bc_in1 = DirichletBC(W.sub_space(0), a_0, boundary_int, 1, method = "pointwise") #Bottom domain interface

And now I have run into the following error. I will be grateful if you can let me know what else am I doing wrong here.

Traceback (most recent call last):
File “poisson5.py”, line 141, in
bc_in1 = DirichletBC(W.sub_space(0), a_0, boundary_int, 1) #Bottom domain interface
File “/usr/local/lib/python3.6/dist-packages/fenics_dolfin-2019.2.0.dev0-py3.6-linux-x86_64.egg/dolfin/fem/dirichletbc.py”, line 131, in init
super().init(*args)
RuntimeError:

*** -------------------------------------------------------------------------
*** DOLFIN encountered an error. If you are not able to resolve this issue
*** using the information listed below, you can ask for help at


*** fenics-support@googlegroups.com


*** Remember to include the error message listed below and, if possible,
*** include a minimal running example to reproduce the error.


*** -------------------------------------------------------------------------
*** Error: Unable to create Dirichlet boundary condition.
*** Reason: User MeshFunction and FunctionSpace meshes are different.
*** Where: This error was encountered inside DirichletBC.cpp.
*** Process: 0


*** DOLFIN version: 2019.2.0.dev0
*** Git changeset: f0a90b8fffc958ed612484cd3465e59994b695f9
*** -------------------------------------------------------------------------

I am guessing the mesh function has to be on the appropriate mesh, to me it seems like boundary_int should be on mesh_bottom. Please read the error messages, as I am only rephrasing:

1 Like

Dear @dokken,

Thank you all for your help till now. I am trying to get a potential profile where the function changes from 1 to 0 in the top sinusoidal domain and remains 0 throughout the bottom sinusoidal domain. Despite my best attempts, I have been unable to get that working. If you can provide a working version, that would be awesome. I have tried the above two modifications and the solution that I am getting is still not correct.

Expected solution:

Simulation results:

Code:

from __future__ import print_function
from dolfin import *
import matplotlib.pyplot as plt
import numpy as np
from ufl import nabla_div
from ufl import sinh
import random

# Width of total domain
width = 100e-6
# Height of total domain
height = 200e-6
# Total resolution
resolution = 100
# Forcing function
f = Constant(0)
# Boundary values for the two equations
a_0 = 0		#Bottom boundary Dirichlet
b_0 = 1		#Top Boundary Dirichlet


#Other properties not used 
# i0 = 12.6
# r = Constant(2*i0)
# iApp = -1
# F = 96485.33
# R = 8.314
# T = 300
# C = Constant(F/(2*R*T))
# kappa = 1
# g = iApp/kappa

tol = 1e-14
# Define SubDomain
class TopDomain(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] >= 0.0001 + 0.00002*sin(5e4*x[0]) + tol

# Define Boundary
class TopBoundary(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary and near(x[1], height)

class BottomBoundary(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary and near(x[1], 0)

class Interface(SubDomain):
    def inside(self, x, on_boundary):
        return near (x[1], 0.0001 + 0.00002*sin(5e4*x[0]))


# Mesh for the entire domain
mesh_full = RectangleMesh(Point(0, 0),
                          Point(width, height),
                          resolution,
                          resolution)

# Define domains
domains = MeshFunction("size_t", mesh_full, 2)
domains.set_all(0)
top_domain = TopDomain()
top_domain.mark(domains, 1)


# Create mesh for the left domain
mesh_top = MeshView.create(domains, 1)
mesh_bottom = MeshView.create(domains, 0)

# Define boundaries

top_boundary = TopBoundary()
bottom_boundary = BottomBoundary()

boundary_top = MeshFunction("size_t", mesh_top, 1)
top_boundary.mark(boundary_top, 1)

boundary_bottom = MeshFunction("size_t", mesh_bottom, 1)
bottom_boundary.mark(boundary_bottom, 1) 


interf = Interface()
boundary_int1 = MeshFunction("size_t", mesh_bottom, 1)
boundary_int1.set_all(0)
interf.mark(boundary_int1, 1)

# interf1 = Interface()
boundary_int2 = MeshFunction("size_t", mesh_top, 1)
boundary_int2.set_all(0)
interf.mark(boundary_int2, 1)

mesh_int1 = MeshView.create(boundary_int1, 1)
mesh_int2 = MeshView.create(boundary_int2, 1)

# for x in mesh.coordinates():
#   if boundary_top.inside(x, True): print('%s is on y = 0' % x)
#   if boundary_bottom.inside(x, True): print('%s is on y = top' % x)
#   if boundary_int1.inside(x, True): print('%s is on y = interface' % x)
#   if boundary_int2.inside(x, True): print('%s is on y = interface' % x)

# Create the function spaces
W_a = FunctionSpace(mesh_bottom, "Lagrange", 1)
W_b = FunctionSpace(mesh_top, "Lagrange", 1)
W = MixedFunctionSpace(W_a, W_b)

# ds_int = ds_bottom(1)
ds_int = Measure("dx", domain=mesh_int1, subdomain_data = boundary_int1)
ds_topMesh = Measure("ds", domain = mesh_top, subdomain_data = boundary_top)
ds_top = ds_topMesh(1) 
# ds_bottom = Measure("ds", domain=mesh_bottom,  subdomain_data=boundary_int)

# print(ds_int)
# print(ds_top)
# print(ds_top)

# # Define boundary conditions
bc_a = DirichletBC(W.sub_space(0), a_0, boundary_bottom, 1)  #Bottom domain
bc_b = DirichletBC(W.sub_space(1), b_0, boundary_top, 1)	#Top domain
bc_in1 = DirichletBC(W.sub_space(0), a_0, boundary_int1, 1)  #Bottom domain interface
bc_in2 = DirichletBC(W.sub_space(1), a_0, boundary_int2, 1) #Top domain interface

bcs = [bc_a, bc_b, bc_in1, bc_in2]	#for Dirichlet condition
# bcs = [bc_a]			#For Neumann condition at top boundary

# Create all of the weak form variables
u = Function(W)
ua, ub = u.sub(0), u.sub(1)
va, vb = TestFunctions(W)

# Need to manually specify these.
dxa = Measure("dx", domain=W.sub_space(0).mesh())
dxb = Measure("dx", domain=W.sub_space(1).mesh())


# The weak formulation
F_inner = inner(grad(ua), grad(va)) * dxa + inner(grad(ub), grad(vb)) * dxb \
            - (f * va * dxa + f * vb * dxb)
        
F = F_inner


solve(F==0, u, bcs, solver_parameters= {"nonlinear_solver": "newton",
               "newton_solver": {"linear_solver": "gmres"}})

# Fi = extract_blocks(F)

# dFi = []
# for J in Fi:
#     for wi in u.split():
#         df = derivative(J, wi)
#         dFi.append(df)
        
# u_sol = u.split()
# problem = MixedNonlinearVariationalProblem(Fi, u_sol, bcs, dFi)
# solver = MixedNonlinearVariationalSolver(problem)
# solver.solve()

# J = derivative(F, u)
# problem = NonlinearVariationalProblem(F, u, bcs, J)
# solver  = NonlinearVariationalSolver(problem)
# solver.solve()

# # ## Export last solution
out_sub0 = File("ua.pvd")
out_sub1 = File("ub.pvd")
# # out_exact = File("poisson-lm-2D-exact.pvd")
out_sub0 << u.sub(0)
out_sub1 << u.sub(1)

Docker output:
fenics@eb5691dd1a41:~/shared/PoissonMixedDimensional$ sudo python3 poissonActual.py
No Jacobian form specified for nonlinear mixed variational problem.
Differentiating residual form F to obtain Jacobian J = F’.
[problem] create list of residual forms OK
[problem] create list of jacobian forms OK, J_list size = 4
[MixedNonlinearVariationalProblem]::check_forms - To be implemented
Solving mixed nonlinear variational problem.
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
Newton iteration 0: r (abs) = 1.005e+01 (tol = 1.000e-10) r (rel) = 1.000e+00 (tol = 1.000e-09)
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
Newton iteration 1: r (abs) = 3.925e-05 (tol = 1.000e-10) r (rel) = 3.905e-06 (tol = 1.000e-09)
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
*** Warning: Found no facets matching domain for boundary condition.
Newton iteration 2: r (abs) = 3.754e-10 (tol = 1.000e-10) r (rel) = 3.735e-11 (tol = 1.000e-09)
Newton solver finished in 2 iterations and 249 linear solver iterations.
fenics@eb5691dd1a41:~/shared/PoissonMixedDimensional$

There are several things you should consider:

  1. Scale your problem: Using mesh coordinates of order 1e-6 will cause issues with bounday marking.
  2. You are not marking the boundaries of the interface correctly. You should visualize your boundary markers in paraview to make sure they are correct. Here is an improvement of your markers:
m __future__ import print_function
from dolfin import *
import matplotlib.pyplot as plt
import numpy as np
from ufl import nabla_div
from ufl import sinh
import random

# Width of total domain
width = 100e-6
# Height of total domain
height = 200e-6
# Total resolution
resolution = 100
# Forcing function
f = Constant(0)
# Boundary values for the two equations
a_0 = 0		#Bottom boundary Dirichlet
b_0 = 1		#Top Boundary Dirichlet



tol = 1e-14
# Define SubDomain
class TopDomain(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] >= 0.0001 + 0.00002*sin(5e4*x[0]) + tol

# Define Boundary
class TopBoundary(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary and near(x[1], height)

class BottomBoundary(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary and near(x[1], 0)

class Interface(SubDomain):
    def inside(self, x, on_boundary):
        return (near (x[1], 0.0001 + 0.00002*sin(5e4*x[0]), 1e-2) and on_boundary and not near(x[0], 0) and not near(x[1], 0, 1e-6) and not near(x[0], width, 1e-16))

class all_boundaries(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary
# Mesh for the entire domain
mesh_full = RectangleMesh(Point(0, 0),
                          Point(width, height),
                          resolution,
                          resolution)

# Define domains
domains = MeshFunction("size_t", mesh_full, 2)
domains.set_all(0)
top_domain = TopDomain()
top_domain.mark(domains, 1)


# Create mesh for the left domain
mesh_top = MeshView.create(domains, 1)
mesh_bottom = MeshView.create(domains, 0)

# Define boundaries

top_boundary = TopBoundary()
bottom_boundary = BottomBoundary()

boundary_top = MeshFunction("size_t", mesh_top, 1)
top_boundary.mark(boundary_top, 1)

boundary_bottom = MeshFunction("size_t", mesh_bottom, 1)
bottom_boundary.mark(boundary_bottom, 1)


interf = Interface()
boundary_int1 = MeshFunction("size_t", mesh_bottom, 1)
boundary_int1.set_all(0)
all_boundaries().mark(boundary_int1, 2)
interf.mark(boundary_int1, 1)
File("mfb.pvd") << boundary_int1
# interf1 = Interface()
boundary_int2 = MeshFunction("size_t", mesh_top, 1)
boundary_int2.set_all(0)

interf.mark(boundary_int2, 1)
File("mf.pvd") << boundary_int2
2 Likes