How to create test and trial functions for MixedFunctionSpace?

Hi everyone,

I am trying to solve a fluid flow problem using MixedFunctionSpace, however, I am not sure how to create trial function and test function for the problem. I try to follow various methods in the forum but always got different errors. Can anyone suggest where in the code am I wrong?

My problem looks like this,

from dolfin import *
import numpy as np
import matplotlib.pyplot as plt

mesh = RectangleMesh(Point(-0.08, -0.02), Point(0.08, 0.02), 160, 80)    
#plot(mesh)

# define meshing parameters
x1 = -0.08
y1 = -0.02
x2 = 0.08
y2 = 0.01
x3 = -0.08
y3 = 0.01
x4 = 0.08
y4 = 0.02

regions = MeshFunction('size_t', mesh, mesh.topology().dim())
boundaries = MeshFunction('size_t', mesh, mesh.topology().dim() - 1)

class Fluid(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] >= 0.01
fluid = Fluid()

class Solid(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] <= 0.01 
solid = Solid()

class Top(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.02)
top = Top()

class LeftF(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], -0.08) and between(x[1], (0.01, 0.02))
leftf = LeftF()

class RightF(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.08) and between(x[1], (0.01, 0.02))
rightf = RightF()

class Interface(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.01)
interface = Interface()

class LeftS(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], -0.08) and between(x[1], (-0.02, 0.01))
lefts = LeftS()

class RightS(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.08) and between(x[1], (-0.02, 0.01))
rights = RightS()

class Bottom(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], -0.02)
bottom = Bottom()

regions.set_all(0)
fluid.mark(regions, 1)
solid.mark(regions, 2)

boundaries.set_all(0)
top.mark(boundaries, 1)
leftf.mark(boundaries, 2)
rightf.mark(boundaries, 3)
interface.mark(boundaries, 4)
lefts.mark(boundaries, 5)
rights.mark(boundaries, 6)
bottom.mark(boundaries, 7)

# define a submesh, composed of region 0 and 1
fluid = MeshFunction('size_t', mesh, mesh.topology().dim())
fluid.set_all(0)
fluid.array()[regions.array() == 1] = 1
submeshfluid = SubMesh(mesh, fluid, 1)

solid = MeshFunction('size_t', mesh, mesh.topology().dim())
solid.set_all(0)
solid.array()[regions.array() == 2] = 2
submeshsolid = SubMesh(mesh, solid, 2)


# define new meshfunctions on this submesh with the same values as their original mesh
fluid_regions = MeshFunction('size_t', submeshfluid, submeshfluid.topology().dim())
fluid_regions.set_all(0)
fluid_boundaries = MeshFunction('size_t', submeshfluid, submeshfluid.topology().dim() - 1)
fluid_boundaries.set_all(0)

solid_regions = MeshFunction('size_t', submeshsolid, submeshsolid.topology().dim())
solid_regions.set_all(0)
solid_boundaries = MeshFunction('size_t', submeshsolid, submeshsolid.topology().dim() - 1)
solid_boundaries.set_all(0)


top.mark(fluid_boundaries, 1)
leftf.mark(fluid_boundaries, 2)
rightf.mark(fluid_boundaries, 3)
#interface.mark(fluid_boundaries, 4)
interface.mark(solid_boundaries, 4)
lefts.mark(solid_boundaries, 5)
rights.mark(solid_boundaries, 6)
bottom.mark(solid_boundaries, 7)


File("2SUBMESHTEST/Results/fluid_boundaries.pvd") << fluid_boundaries
File("2SUBMESHTEST/Results/solid_boundaries.pvd") << solid_boundaries
dsf = Measure("ds")[fluid_boundaries]
dss = Measure("ds")[solid_boundaries]

UF = VectorFunctionSpace(submeshfluid, 'P', 2)
PF = FunctionSpace(submeshfluid, 'P', 1)
TS = FunctionSpace(submeshsolid, 'P', 1)
W = MixedFunctionSpace(UF, PF, TS)

(v,q,z) = TestFunctions(W)
w = Function(W)
u = w.sub(0)
p = w.sub(1)

dxf = Measure("dx")[fluid_regions]
dxs = Measure("dx")[solid_regions]


bcs = [DirichletBC(W.sub_space(0).sub(0), Constant(0), top),   
       DirichletBC(W.sub_space(0).sub(1), Constant(0), top),
       DirichletBC(W.sub_space(0).sub(0), Constant(0), interface),   
       DirichletBC(W.sub_space(0).sub(1), Constant(0), interface),
       DirichletBC(W.sub_space(1), Constant(80), leftf),
       DirichletBC(W.sub_space(1), Constant(0), rightf)] 

# Surface normal
nf = FacetNormal(submeshfluid)
ns = FacetNormal(submeshsolid)

#############################
## for solving steady flow ##
#############################
# Define parameters used in the weak form
mu  = 0.89
rho = 1000
k   = 0.6071
cp  = 4220
alp = k/rho/cp
muS  = 1
rhoS = 3000
kS   = 1.5
cpS  = 220
alpS = kS/rhoS/cpS
Fl   = 0.00005

# Weak form of the steady state fluid problem
F = rho*inner(grad(u)*u, v)*dxf \
    + mu*inner(grad(u), grad(v))*dxf \
    + inner(grad(p),v)*dxf \
    - div(u)*q*dxf    
# Jacobian matrix to be supplied to the non linear solver
J = derivative(F, w) # Jacobian   
# Call the non'linear solver
solve(F == 0, w, bcs, J=J)
#############################

# Plot solution
plt.figure()
a = plot(u)
plt.colorbar(a)

plt.figure()
b = plot(p)
plt.colorbar(b)

I am not sure why no one reply to this post, is the question is not understandable or I am breaking any rule of the forum?

Dear @phurisja,
This forum is community driven, and we are receiving many posts every day. We do not have the capability to answer people within 24 hours in every case.

2 Likes

Thank you very much. That is understandable.

Hi,

First of all, you mention using various methods and getting various errors without more details. That might be helpful for people trying to reproduce the issue.

To answer the main question, the way you define test and trial functions from a MixedFunctionSpace is correct.

Some comments on your code :
1- I recommend using MeshView instead of SubMesh, using directly the cell MeshFunction regions :

submeshfluid = MeshView.create(regions, 1)
submeshsolid = MeshView.create(regions, 2)

2- I don’t see why do you define fluid_regions and solid_regions. Since these MeshFunctions are both set to zero, it doesn’t make sense in the Measures dxf and dxs you define later. Plus note that this way of define the Measure is deprecated (you should get a warning saying exactly that). If you want to define the Measures corresponding to these submeshes, you should use :

dxf = Measure("dx", domain=submeshfluid)
dxs = Measure("dx", domain=submeshsolid)

Same remark for the ds measures for the submeshes boundaries :

dsf = Measure("ds", domain=submeshfluid, subdomain_data = fluid_boundaries)
dss = Measure("ds", domain=submeshsolid, subdomain_data = solid_boundaries)

3- The Jacobian has to be defined by blocks. Note that it can be automatically computed by the solve function using solve(F == 0, w, bcs), but if you want to define it manually then I suggest you have a look to this recent post.

2 Likes

Hi, thank a lot for your reply.
According to the second point of your comment, I did that because I was following an example and I am not clearly understand the point of doing it, so I leave it the way it is.
Anyway, I will try to fix my code according to your suggestion.
Thanks again for your comments.

I have fixed the code and it now look likes the below snippet.

from dolfin import *
import numpy as np
import matplotlib.pyplot as plt

mesh = RectangleMesh(Point(-0.08, -0.02), Point(0.08, 0.02), 160, 80)    
#plot(mesh)

# define meshing parameters
x1 = -0.08
y1 = -0.02
x2 = 0.08
y2 = 0.01
x3 = -0.08
y3 = 0.01
x4 = 0.08
y4 = 0.02

class Fluid(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] >= 0.01
fluid = Fluid()

class Solid(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] <= 0.01 
solid = Solid()

class Top(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.02)
top = Top()

class LeftF(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], -0.08) and between(x[1], (0.01, 0.02))
leftf = LeftF()

class RightF(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.08) and between(x[1], (0.01, 0.02))
rightf = RightF()

class Interface(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.01)
interface = Interface()

class LeftS(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], -0.08) and between(x[1], (-0.02, 0.01))
lefts = LeftS()

class RightS(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.08) and between(x[1], (-0.02, 0.01))
rights = RightS()

class Bottom(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], -0.02)
bottom = Bottom()


regions = MeshFunction('size_t', mesh, mesh.topology().dim())
boundaries = MeshFunction('size_t', mesh, mesh.topology().dim() - 1)

regions.set_all(0)
fluid.mark(regions, 1)
solid.mark(regions, 2)

boundaries.set_all(0)
top.mark(boundaries, 1)
leftf.mark(boundaries, 2)
rightf.mark(boundaries, 3)
interface.mark(boundaries, 4)
lefts.mark(boundaries, 5)
rights.mark(boundaries, 6)
bottom.mark(boundaries, 7)

# define a submesh, composed of region 0 and 1
submeshfluid = MeshView.create(regions, 1)
submeshsolid = MeshView.create(regions, 2)
dxf = Measure("dx", domain=submeshfluid)
dxs = Measure("dx", domain=submeshsolid)

# define new meshfunctions on this submesh with the same values as their original mesh
fluid_boundaries = MeshFunction('size_t', submeshfluid, submeshfluid.topology().dim() - 1)
fluid_boundaries.set_all(0)
solid_boundaries = MeshFunction('size_t', submeshsolid, submeshsolid.topology().dim() - 1)
solid_boundaries.set_all(0)
dsf = Measure("ds", domain=submeshfluid, subdomain_data = fluid_boundaries)
dss = Measure("ds", domain=submeshsolid, subdomain_data = solid_boundaries)

top.mark(fluid_boundaries, 1)
leftf.mark(fluid_boundaries, 2)
rightf.mark(fluid_boundaries, 3)
#interface.mark(fluid_boundaries, 4)
interface.mark(solid_boundaries, 4)
lefts.mark(solid_boundaries, 5)
rights.mark(solid_boundaries, 6)
bottom.mark(solid_boundaries, 7)


UF = VectorFunctionSpace(submeshfluid, 'P', 2)
PF = FunctionSpace(submeshfluid, 'P', 1)
TS = FunctionSpace(submeshsolid, 'P', 1)
W = MixedFunctionSpace(UF, PF, TS)

(v,q,z) = TestFunctions(W)
w = Function(W)
u = w.sub(0)
p = w.sub(1)
t = w.sub(2)

bcs = [DirichletBC(W.sub_space(0).sub(0), Constant(0), top),   
       DirichletBC(W.sub_space(0).sub(1), Constant(0), top),
       DirichletBC(W.sub_space(0).sub(0), Constant(0), interface),   
       DirichletBC(W.sub_space(0).sub(1), Constant(0), interface),
       DirichletBC(W.sub_space(1), Constant(80), leftf),
       DirichletBC(W.sub_space(1), Constant(0), rightf)] 

# Surface normal
nf = FacetNormal(submeshfluid)
ns = FacetNormal(submeshsolid)

#############################
## for solving steady flow ##
#############################
# Define parameters used in the weak form
mu  = 0.89
rho = 1000
k   = 0.6071
cp  = 4220
alp = k/rho/cp
muS  = 1
rhoS = 3000
kS   = 1.5
cpS  = 220
alpS = kS/rhoS/cpS
Fl   = 0.00005

# Weak form of the steady state fluid problem
F = rho*inner(grad(u)*u, v)*dxf \
    + mu*inner(grad(u), grad(v))*dxf \
    + inner(grad(p),v)*dxf \
    - div(u)*q*dxf    
  
# Call the non'linear solver
solve(F == 0, w, bcs)
#############################

(u, p, t) = w.split(True)

# Plot solution
plt.figure()
a = plot(u)
plt.colorbar(a)

plt.figure()
b = plot(p)
plt.colorbar(b)

However, the problem is still not solved. The error that I got is,

No Jacobian form specified for nonlinear mixed variational problem.
Differentiating residual form F to obtain Jacobian J = F'.
Traceback (most recent call last):
  File "Submeshtest.py", line 153, in <module>
    solve(F == 0, w, bcs)
  File "/usr/local/lib/python3.6/dist-packages/fenics_dolfin-2019.2.0.dev0-py3.6-linux-x86_64.egg/dolfin/fem/solving.py", line 233, in solve
    _solve_varproblem(*args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/fenics_dolfin-2019.2.0.dev0-py3.6-linux-x86_64.egg/dolfin/fem/solving.py", line 294, in _solve_varproblem
    form_compiler_parameters=form_compiler_parameters)
  File "/usr/local/lib/python3.6/dist-packages/fenics_dolfin-2019.2.0.dev0-py3.6-linux-x86_64.egg/dolfin/fem/problem.py", line 199, in __init__
    assert(len(J) == len(u) * len(u))
AssertionError

At this point, I I am not sure what the problem is, do I need to import some more libraries?

p.s. when I leave the Jacobian line as in the above code,

# Jacobian matrix to be supplied to the non linear solver
J = derivative(F, w) # Jacobian   
# Call the non'linear solver
solve(F == 0, w, bcs, J=J)

I got the error AttributeError: ‘Function’ object has no attribute ‘_cpp_object’ I guess this is the consequence of not defining the Jacobian by blocks.

This error means that your Jacobian doesn’t have the right number of blocks.
Your MixedFunctionSpace is composed of three spaces, so your Jacobian should be a 3 x 3 block matrix. But your variational formulation only involves variables from the 2 first spaces.

1 Like

I have tried to deleted the last space in the code and run it. I got the following error. Is this because I am not supposed to use the MixedFunctionSpace when it is possible to use MixedElement?

Traceback (most recent call last):
  File "Submeshtest.py", line 147, in <module>
    solve(F == 0, w, bcs)
  File "/usr/local/lib/python3.6/dist-packages/fenics_dolfin-2019.2.0.dev0-py3.6-linux-x86_64.egg/dolfin/fem/solving.py", line 233, in solve
    _solve_varproblem(*args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/fenics_dolfin-2019.2.0.dev0-py3.6-linux-x86_64.egg/dolfin/fem/solving.py", line 298, in _solve_varproblem
    solver.solve()
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 set given (local) rows to identity matrix.
*** Reason:  some diagonal elements not preallocated (try assembler option keep_diagonal).
*** Where:   This error was encountered inside PETScMatrix.cpp.
*** Process: 0
*** 
*** DOLFIN version: 2019.2.0.dev0
*** Git changeset:  f0a90b8fffc958ed612484cd3465e59994b695f9
*** -------------------------------------------------------------------------

Because the above trials are still not working, I have stepped back a bit. I tried to modify the code from this demo_block-assembly-2D2D-nonlinear.py
I works when I use the governing equation with only 1 unknown function (e.g. heat conduction) as in the below snippet.

from dolfin import *

class Top(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 1.0)
    
class Left1(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.0) and between(x[1], (0.5, 1))

class Right1(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 1.0) and between(x[1], (0.5, 1))
 
class Interface(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.5)
    
class Left2(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.0) and between(x[1], (0.0, 0.5))

class Right2(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 1.0) and between(x[1], (0.0, 0.5))
    
class Bottom(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.0)
    
class Upper(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] >= 0.5
upper = Upper()

class Lower(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] <= 0.5 
lower = Lower()
 
def newton_solver_parameters():
    return{"nonlinear_solver": "newton",
           "newton_solver": {"linear_solver": "gmres"}}

# Create meshes
n = 16
mesh = UnitSquareMesh(n, n)
 
regions = MeshFunction('size_t', mesh, mesh.topology().dim())
regions.set_all(0)
upper.mark(regions, 1)
lower.mark(regions, 2)

submesh1 = MeshView.create(regions, 1)
submesh2 = MeshView.create(regions, 2)

submesh1_boundaries = MeshFunction('size_t', submesh1, submesh1.topology().dim() - 1)
submesh1_boundaries.set_all(0)
submesh2_boundaries = MeshFunction('size_t', submesh2, submesh2.topology().dim() - 1)
submesh2_boundaries.set_all(0)
ds1 = Measure("ds", domain=submesh1, subdomain_data = submesh1_boundaries)
ds2 = Measure("ds", domain=submesh2, subdomain_data = submesh2_boundaries)

Left1().mark(submesh1_boundaries, 2)
Right1().mark(submesh1_boundaries, 3)
Left2().mark(submesh2_boundaries, 5)
Right2().mark(submesh2_boundaries, 6)

# Create function spaces
W1 = FunctionSpace(submesh1, "Lagrange", 1)
W2 = FunctionSpace(submesh2, "Lagrange", 1)
# Define the mixed function space W = W1 x W2
W = MixedFunctionSpace( W1, W2 )


bc1 = DirichletBC(W1, Constant(80), Top())
bc2 = DirichletBC(W2, Constant(30), Bottom())
bc3 = DirichletBC(W1, Constant(20), Interface())
bc4 = DirichletBC(W2, Constant(20), Interface())
bcs = [bc1,bc2,bc3,bc4]


# Define mixed-domains variational problem
(v1,v2) = TestFunctions(W)
u = Function(W)
u1 = u.sub(0)
u2 = u.sub(1)

dx1 = Measure("dx", domain=W.sub_space(0).mesh())
dx2 = Measure("dx", domain=W.sub_space(1).mesh()) 

# Neumann BCs
q1 = 9*(20-u1)
q2 = 9*(20-u2)

F1 = inner(grad(u1), grad(v1))*dx1 - q1*v1*ds1(2) - q1*v1*ds1(3)
F2 = inner(grad(u2), grad(v2))*dx2 - q2*v2*ds2(5) - q2*v2*ds2(6)
F = F1+F2

# Compute solution - ref monodomain problems
#solve(F1 == 0, u1, bc1)
#u1_ref = u1.copy(deepcopy=True)
#solve(F2 == 0, u2, bc2)
#u2_ref = u2.copy(deepcopy=True)

# Compute solution - mixed-domains problem
solve(F == 0, u, bcs, solver_parameters=newton_solver_parameters())
# solve(F == 0, u, bcs, solver_parameters={"nonlinear_solver":"snes"}) # Not available yet

u1_ref = u1.copy(deepcopy=True)
u2_ref = u2.copy(deepcopy=True)

ufile = File("Test4/u1.pvd")
ufile << u1_ref
ufile = File("Test4/u2.pvd")
ufile << u2_ref

After this works, I extend to the problem with more than 1 unknown function (e.g. fluid flow problem with velocity and pressure as the unknowns) as in the below snippet

from dolfin import *

class Top(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 1.0)
    
class Left1(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.0) and between(x[1], (0.5, 1))

class Right1(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 1.0) and between(x[1], (0.5, 1))
 
class Interface(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.5)
    
class Left2(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.0) and between(x[1], (0.0, 0.5))

class Right2(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 1.0) and between(x[1], (0.0, 0.5))
    
class Bottom(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.0)
    
class Fluid(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] >= 0.5
fluid = Fluid()

class Solid(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] <= 0.5 
solid = Solid()
 
def newton_solver_parameters():
    return{"nonlinear_solver": "newton",
           "newton_solver": {"linear_solver": "gmres"}}

# Create meshes
n = 16
mesh = UnitSquareMesh(n, n)

regions = MeshFunction('size_t', mesh, mesh.topology().dim())
regions.set_all(0)
fluid.mark(regions, 1)
solid.mark(regions, 2)

submesh1 = MeshView.create(regions, 1)
submesh2 = MeshView.create(regions, 2)

submesh1_boundaries = MeshFunction('size_t', submesh1, submesh1.topology().dim() - 1)
submesh1_boundaries.set_all(0)
submesh2_boundaries = MeshFunction('size_t', submesh2, submesh2.topology().dim() - 1)
submesh2_boundaries.set_all(0)
ds1 = Measure("ds", domain=submesh1, subdomain_data = submesh1_boundaries)
ds2 = Measure("ds", domain=submesh2, subdomain_data = submesh2_boundaries)

Left1().mark(submesh1_boundaries, 2)
Right1().mark(submesh1_boundaries, 3)
Left2().mark(submesh2_boundaries, 5)
Right2().mark(submesh2_boundaries, 6)

# Create function spaces
U1 = VectorFunctionSpace(submesh1, "Lagrange", 2)
P1 = FunctionSpace(submesh1, "Lagrange", 1)
U2 = VectorFunctionSpace(submesh2, "Lagrange", 2)
P2 = FunctionSpace(submesh2, "Lagrange", 1)
# Define the mixed function space W = W1 x W2
W = MixedFunctionSpace(U1, P1, U2, P2)

# Define boundary conditions
bc1 = DirichletBC(U1, Constant((0,0)), Top())
bc2 = DirichletBC(U1, Constant((0,0)), Interface())
bc3 = DirichletBC(U2, Constant((0,0)), Interface())
bc4 = DirichletBC(U2, Constant((0,0)), Bottom())
bc5 = DirichletBC(P1, Constant(8), Left1())
bc6 = DirichletBC(P1, Constant(0), Right1())
bc7 = DirichletBC(P2, Constant(8), Left2())
bc8 = DirichletBC(P2, Constant(0), Right2())
#bc1 = [bc1,bc2,bc3,bc4]
bcs = [bc1,bc2,bc3,bc4,bc5,bc6,bc7,bc8]

# Define mixed-domains variational problem
(v1, q1, v2, q2) = TestFunctions(W)
u = Function(W)
u1 = u.sub(0)
p1 = u.sub(1)
u2 = u.sub(2)
p2 = u.sub(3)

dx1 = Measure("dx", domain=W.sub_space(0).mesh())
dx2 = Measure("dx", domain=W.sub_space(2).mesh()) 

# Neumann BCs
#q1 = 9*(20-u1)
#q2 = 9*(20-u2)

# Fluid properties
rho = 1000
mu = 0.89

F1 = rho*inner(grad(u1)*u1, v1)*dx1 + mu*inner(grad(u1), grad(v1))*dx1 + inner(grad(p1),v1)*dx1 - div(u1)*q1*dx1 
F2 = rho*inner(grad(u2)*u2, v2)*dx2 + mu*inner(grad(u2), grad(v2))*dx2 + inner(grad(p2),v2)*dx2 - div(u2)*q2*dx2
F = F1+F2

# Compute solution - ref monodomain problems
#solve(F1 == 0, u1, bc1)
#u1_ref = u1.copy(deepcopy=True)
#p1_ref = p1.copy(deepcopy=True)
#solve(F2 == 0, u2, bc2)
#u2_ref = u2.copy(deepcopy=True)
#p2_ref = p2.copy(deepcopy=True)

# Compute solution - mixed-domains problem
solve(F == 0, u, bcs, solver_parameters=newton_solver_parameters())
# solve(F == 0, u, bcs, solver_parameters={"nonlinear_solver":"snes"}) # Not available yet

u1_ref = u1.copy(deepcopy=True)
p1_ref = p1.copy(deepcopy=True)
u2_ref = u2.copy(deepcopy=True)
p2_ref = p2.copy(deepcopy=True)

ufile = File("Test5/u1.pvd")
ufile << u1_ref
ufile = File("Test5/p1.pvd")
ufile << p1_ref
ufile = File("Test5/u2.pvd")
ufile << u2_ref
ufile = File("Test5/p2.pvd")
ufile << p2_ref

However I am not sure about these lines

dx1 = Measure("dx", domain=W.sub_space(0).mesh())
dx2 = Measure("dx", domain=W.sub_space(2).mesh()) 

I am not sure what does the W.sub_space() mean.
Does it mean the following?
W.sub_space(0) = U1
W.sub_space(1) = P1
W.sub_space(2) = U2
W.sub_space(3) = P2

If so, how should I define the dx1 and dx2?
I believe that dx1 and dx2 should be consisted of W.sub_space(0), W.sub_space(1) and W.sub_space(2), W.sub_space(3) respectively.

try this

UF = VectorElement(“P”, submeshfluid.ufl_cell(), 1, dim=2)
PF = FiniteElement(“P”, submeshfluid.ufl_cell(), 1)
TS = FiniteElement(“P”, submeshsolid.ufl_cell(), 1)
W = FunctionSpace(mesh, MixedElement([UF, PF, TS]))

instead of :
UF = VectorFunctionSpace(submeshfluid, ‘P’, 2)
PF = FunctionSpace(submeshfluid, ‘P’, 1)
TS = FunctionSpace(submeshsolid, ‘P’, 1)
W = MixedFunctionSpace(UF, PF, TS)

in your first code

1 Like

Thank you for your reply.
However, I just figure out that the definition of the mixedfunctionspace in my first code is also correct (for sure, there are other ways of defining it, e.g. the way you have suggested me).
What is wrong in my first code is the definition of the weak form, I fixed it to the following snippet, (and now it works)

from dolfin import *

class Top(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 1.0)
    
class Left1(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.0) and between(x[1], (0.5, 1))

class Right1(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 1.0) and between(x[1], (0.5, 1))
 
class Interface(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.5)
    
class Left2(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.0) and between(x[1], (0.0, 0.5))

class Right2(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 1.0) and between(x[1], (0.0, 0.5))
        
class Bottom(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[1], 0.0)
    
# Create meshes
n = 50
mesh = UnitSquareMesh(n, n)

class Upper(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] >= 0.5
upper = Upper()

class Lower(SubDomain):
    def inside(self, x, on_boundary):
        return x[1] <= 0.5 
lower = Lower()

regions = MeshFunction('size_t', mesh, mesh.topology().dim(), 0)
upper.mark(regions, 1)
lower.mark(regions, 2)

boundaries = MeshFunction('size_t', mesh, mesh.topology().dim() - 1, 0)
Interface().mark(boundaries,1)

mesh_U = MeshView.create(regions, 1)
mesh_L = MeshView.create(regions, 2)

# Function spaces
U_U  = VectorFunctionSpace(mesh_U, "Lagrange", 2) # Velocity on the upper domain
P_U  = FunctionSpace(mesh_U, "Lagrange", 1)       # Pressure on the upper domain
# Define the mixed function space W = W1 x W2
W = MixedFunctionSpace(U_U, P_U)

# Markers for Dirichlet and Neuman bcs
ff_U = MeshFunction("size_t", mesh_U, mesh_U.geometry().dim()-1, 0)
Top().mark(ff_U, 1)
Left1().mark(ff_U, 2)
Right1().mark(ff_U, 3)
Interface().mark(ff_U, 4)

p_left = 80.0
p_right = 0.0
pressure_left = Constant(p_left)
pressure_right = Constant(p_right)

# Define boundary conditions for upper part
bc1 = DirichletBC(U_U, Constant((0.0,0.0)), ff_U, 1)
bc2 = DirichletBC(U_U, Constant((0.0,0.0)), ff_U, 4) 
bc3 = DirichletBC(P_U, pressure_left, ff_U, 2)
bc4 = DirichletBC(P_U, pressure_right, ff_U, 3)
bcU = [bc1,bc2,bc3,bc4]

# Function and test functions
(uu,pu) = TrialFunctions(W)
(vu,qu) = TestFunctions(W)

u = Function(W)
uu = u.sub(0)
pu = u.sub(1)

dx_U = Measure("dx", domain=W.sub_space(0).mesh())
ds_U  = Measure("ds", domain=W.sub_space(0).mesh(), subdomain_data=ff_U)

# Parameters for the variational problem
mu  = 0.89
rho = 1000
k   = 0.6071
cp  = 4220
alp = k/rho/cp


nu = FacetNormal(mesh_U)
nl = FacetNormal(mesh_L)
force = Constant((0.0, 0.0))

F = rho*inner(grad(uu)*uu, vu)*dx_U + mu*inner(grad(uu), grad(vu))*dx_U \
     - pu*div(vu)*dx_U + qu*div(uu)*dx_U + Constant(0.0)*inner(pu,qu)*dx_U \
     - inner(force, vu)*dx_U \
     + pressure_left*inner(nu, vu)*ds_U(2) \
     + pressure_right*inner(nu, vu)*ds_U(3)

# solving the problem     
solve(F == 0, u, bcU)
out_uu = File("V.pvd")
out_pu = File("P.pvd")
out_uu << u.sub(0)
out_pu << u.sub(1)

But I do have further question regarding to the weak form, I dont know where does the term Constant(0.0)*inner(pu,qu)*dx_U come from, it seems trivial but the solution will not converge without this term.
I got this weak form from the file named “stokes_no_traction_bcs.py” in this link

1 Like

Sorry for late reply. I faced this problem several times, but I still could not figure out what is the reason for that.

1 Like