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

Defining Lagrange's multiplier field ONLY on boundaries

+2 votes

Hello!

My question is close related with the topic

http://scicomp.stackexchange.com/questions/7204/poisson-equation-impose-full-gradient-as-boundary-condition-via-lagrange-multip

which was discussed in another forum. Shortly, I need to impose a general non-linear boundary condition (given as a restriction R(u) = 0) on certain boundary and the best way is to define a Lagrange multiplier field (lm) on the boundary. As a test, I supose my restriction is a simple Dirichlet bc "u = u_bot" => "R(u) = u - u_bot" on a specific booundary. My implementation of the problem is the following:

from dolfin import *
import numpy
set_log_level(ERROR)

#-------------- Preprocessing step -----------------

# Create mesh and define function space
mesh = UnitSquareMesh(10, 10)

V = FunctionSpace(mesh, 'Lagrange', 1)
#print V.print_dofmap()

# Define boundary segments for the boundary condition via Lagrange multiplier

# Create mesh function over cell facets
boundary_parts = MeshFunction("size_t", mesh, mesh.topology().dim()-1)

# Mark upper boundary facets as subdomain 0
class UpperBoundary(SubDomain):
    def inside(self, x, on_boundary):
        tol = 1E-14   # tolerance for coordinate comparisons
        return on_boundary and abs(x[1] - 1) < tol

Gamma_Up = UpperBoundary()
Gamma_Up.mark(boundary_parts, 0)


# Mark lower boundary facets as subdomain 1
class LowerBoundary(SubDomain):
    def inside(self, x, on_boundary):
        tol = 1E-14   # tolerance for coordinate comparisons
        return on_boundary and abs(x[1]) < tol
    def map(self, x, y):
    y = x

Gamma_Low = LowerBoundary()
Gamma_Low.mark(boundary_parts, 1)

# Create function space over Lower boundary edges???
V_lm = FunctionSpace(mesh, 'Lagrange', 1, constrained_domain = Gamma_Low)
#print V_lm.print_dofmap()

#Mixed finite element space
W = V * V_lm
print W


#-------------- Solution and problem definition step -----------------
# given mesh and boundary_parts

u_up = Constant(373.15)
bcs = [DirichletBC(W.sub(0), u_up, Gamma_Up)]

# Define variational problem
f_e = Expression("1000")
u_bot = Expression("273.15")
#expression = -inner(grad(u), grad(v))*dx - v*f_e*dx - v*f_e*ds
(u , lm)  = TrialFunctions(W)
(v, v_lm) = TestFunctions(W)
expression = -inner(grad(u), grad(v))*dx + (lm*v + v_lm*(u-u_bot))*ds(1) - v*f_e*dx

a = lhs(expression)
L = rhs(expression)


# Compute solution
A = assemble(a, exterior_facet_domains=boundary_parts)
b = assemble(L, exterior_facet_domains=boundary_parts)
for bc in bcs: bc.apply(A, b)

U = Function(W)
solve(A, U.vector(), b, 'lu')
u, lm = U.split()

#Plotting
plot(u,interactive=True)
plot(lm,interactive=True)

file = File("Temperature.pvd")
file << u  

For the formulation works, "lm" and its test functions "v_lm" must be defined only on the boundary ds(1). That its, "lm" must have a DofMap restricted only to the mesh nodes lying on the boundary "1". This problem run cleanly for coarse meshes (7x7) but for finer the solution is "nan" (see the file after run). I suspect that the system is singular, because the DofMap for "lm" is actually not restricted to the specified boundary; as result, the formulation is giving zero rows in the system matrix because there is nothing to assembly in the interior nodes for the variable "lm".

So, my questions are:

  • Am I doing a right definition of the "constrained_domain" space for the Lagrange Multipliers field? I use a identity mapping "x = y" in the class LowerBoundary, because I do not wish to do any special mapping with the boundary.
  • There is another way to define a Lagrange multiplier field restricted only to a specific boundary of the domain?

Thanks!

Diego

asked Jul 22, 2013 by DiegoM FEniCS Novice (330 points)

What is the application? I have been looking for PDEs with nonlinear BCs for quite some time. See this post...

Thanks Jan for your answer.

I am actually doing simulations of fluid flow in presence of free surfaces (interfacial flows). Then, I have to take account of the evolution of the free surface and adapt the domain and mesh accordingly. My approach is to solve the problem in a fully coupled and implicit way: inside a Newton's step, I update simultaneously all the variables: velocity, pressure, nodal positions, surfactant concentration (if present), etc.

The Lagrange's multiplier approach is very interesting because it allow to impose strong non-linear boundary conditions, as for example kinematic type conditions at interfaces, were the normal nodal velocities must equal the fluid velocity: dot(v-dxdt, n) = 0
Another similar example is the impermeability condition at solid walls, which implies that the normal fluid velocity is null: dot(v,n) = 0. This condition can be tricky to apply on boundaries which normal n does not coincides with some of the coordinates.

Using a Lagrange multiplier we impose the boundary condition by controlling the normal flux of the property, in such a way to get the desired values on the boundary. Then, it is similar to impose a Robin-type condition as is discussed in the post you cite.

In the example I posted, the Lagrange multiplier "lm" represents the heat flux normal to the boundary, which is adjusted by the restriction "R = u - u_bot". In my applications, the Lagrange multipliers represents the fluid traction (normal stress) or a pseudo-stress that control the node displacements on the boundaries. Details about the method and its implementation can be seen for example in:

J.E. Sprittles and Y.D. Shikhmurzaev, 2012.
Finite element framework for describing dynamic wetting phenomena.
International Journal for Numerical Methods in Fluids, 68, 1257-1298.

In conclusion, the method is very general while we pay the cost of some additional degrees of freedoms of the boundaries: the Lagrange's multiplier DOFs.

Best,
Diego

Thanks for the explanations. Btw., there is a preprint of the paper you have mentioned on ArXiv. I fully agree with the advantages of using multipliers in general frameworks. However, I could not find any nonlinearities in the BCs... (?)

In the case solved on my posted code, you are right, there are not non-linearities in the restriction R = u - u_bot

In case of kinematic conditions, like for example R = dot(u - dxdt, n), the coordinates x are actually variables solved simultaneously with the flow field. The normal vector n also depends on spatial derivatives of x. Thus, the dot product between dxdt and n set one of the non-linear boundary conditions. There are similar conditions for kinematic equations in case of impervious restrictions on walls and so on.

Best,
Diego

1 Answer

+2 votes
 
Best answer

I believe that mixing spaces on different domains is still not supported but is on plan. As a workaround you can constraint your Lagrange multiplier space by applying DirichletBC on redundant DOFs.

answered Jul 22, 2013 by Jan Blechta FEniCS Expert (51,420 points)
selected Aug 8, 2013 by Jan Blechta

Thanks Jan for the information!

I had hope when read in the release notes for the version 1.1 (http://fenicsproject.org/releases/1.1/index.html):
- Support for restricted function spaces

Appearently, the support is not ready yet and/or I am doing something wrong in the definition of the "restricted FunctionSpace using the "constrained_domain" option. I am not sure about it and the documentation is not clear for me.

Regarding your suggestion to "constrain" the unused DOFs of the Lagrange's multiplier space by mean DirichletBC, it work in the sense that avoid a singular system.

However, it is obvious now that
the solution values on the desired boundary depends STRONGLY on the values assigned as BC to the field, which make invalid the proposed methodology.

Hopefully the support for restricted funtion spaces be ready soon, because it will be very useful and certainly it will increase enormously the power of this really good package.

Thanks!

However, it is obvious now that
the solution values on the desired boundary depends STRONGLY on the values assigned as BC to the field, which make invalid the proposed methodology.

You're wrong. Your original form

-inner(grad(u), grad(v))*dx + (lm*v + v_lm*(u-u_bot))*ds(1) - v*f_e*dx

depend only on values of lm and v_lm on facets marked 1. You define

lm_constraint = DirichletBC(W.sub(1), 0.0, "abs(x[1])>2.0*DOLFIN_EPS")

to constraint all unused values of lm. If properly defined it does not constraint any values on facets marked 1. Note that definition

lm_constraint = DirichletBC(W.sub(1), 0.0, boundary_parts, 0)

would not work. There may be necessary to use DirichletBC(..., method="pointwise") in special cases to avoid facet-wise search of DOFs. Generally user must be precise in that.

Alternatively you may use GenericMatrix.ident_zeros() feature to make your matrix regular. But I don't recommend it as it may obfuscate another issues in your code. It also may not work with DOLFIN version $\le$ 1.2.0 and PETSc backend.

I am trying to implement the command you suggested:

lm_constraint = DirichletBC(W.sub(1), 0.0, "abs(x[1])>2.0*DOLFIN_EPS")

but I obtain the following error:

Calling DOLFIN just-in-time (JIT) compiler, this may take some time.
In instant.recompile: The module did not compile with command 'make VERBOSE=1 ', see '/home/diego/.instant/error/dolfin_compile_code_c3dcf5394f8862b894b254bdae657d97/compile.log'
Traceback (most recent call last):
  File "published_code.py", line 74, in <module>
    lm_constraint = DirichletBC(W.sub(1), 0.0, "abs(x[1] > 2*DOLFIN_EPS)")
  File "/usr/lib/python2.7/dist-packages/dolfin/fem/bcs.py", line 99, in __init__
    sub_domain = compile_subdomains(args[2])
  File "/usr/lib/python2.7/dist-packages/dolfin/compilemodules/subdomains.py", line 180, in compile_subdomains
    subdomains = compile_subdomain_code("\n".join(all_code), classnames)
  File "/usr/lib/python2.7/dist-packages/dolfin/compilemodules/subdomains.py", line 114, in compile_subdomain_code
    compiled_module = compile_extension_module(code)
  File "/usr/lib/python2.7/dist-packages/dolfin/compilemodules/jit.py", line 66, in mpi_jit
    return local_jit(*args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/dolfin/compilemodules/compilemodule.py", line 452, in compile_extension_module
    **instant_kwargs)
  File "/usr/lib/python2.7/dist-packages/instant/build.py", line 541, in build_module
    recompile(modulename, module_path, new_compilation_checksum, build_system)
  File "/usr/lib/python2.7/dist-packages/instant/build.py", line 150, in recompile
    instant_error(msg % (cmd, compile_log_filename_dest))
  File "/usr/lib/python2.7/dist-packages/instant/output.py", line 49, in instant_error
    raise RuntimeError(text)
RuntimeError: In instant.recompile: The module did not compile with command 'make VERBOSE=1 ', see '/home/diego/.instant/error/dolfin_compile_code_c3dcf5394f8862b894b254bdae657d97/compile.log'

I take a look on the file "compile.log". The first lines are:

-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Found SWIG: /usr/bin/swig2.0 (found version "2.0.4")
-- Configuring done
-- Generating done
CMake Warning:
  Manually-specified variables were not used by the project:

    DEBUG

Do you have any idea about the problem?

Thanks!

CMake Warning:
  Manually-specified variables were not used by the project:

    DEBUG

is only warning which did not cause module not to compile. Search below for an error.

...