Catenoids in Morpho

Catenoid soap films are a neat example of shape optimization that is readily observable using day-to-day materials. Fixed between two parallel rings close to each other, the soap film minimizes its surface area (due to surface tension). Depending the separation-to-diameter ration, this `minimal surface' is not cylindrical, but a curved catenoid in shape.

While there are a lot of intricacies I am not mentioning here (and this is a field of active research), let's checkout a simple computation of the shape through explicit minimization of the area using Morpho.

We first set up the domain.

import meshtools

import plot

import optimize


var r = 1.0 // radius

var ratio = 0.4 // Separation to diameter ratio

var L = 2*r*ratio // Separation


// Generate a tube / cylindrical mesh

var mesh = AreaMesh(fn (u, v) [r*cos(u), v, r*sin(u)],

-Pi...Pi:Pi/10,

-L/2..L/2:L/5,

closed=[true,false]

)


mesh.addgrade(1)


// Select the boundary

var bnd = Selection(mesh, boundary=true)


var g = plotselection(mesh, bnd, grade=1)

g.title = "Before"

Show(g)

Figure showing the initial triagulated mesh with boundaries highlighted.

Initial mesh with boundary edges highlighted

This creates the mesh as shown above. Note that the output of Show is an interactive one, and I have shown a prettier but static ray-traced version here. We can get to making these figures using the povray library in a later post.

The red edges are highlighting the boundary selection. This is crucial, since we need to tell our optimizer to fix the boundaries, else the film would collapse, as would happen if the soap film loses contact with either of the rings.

Now, we define the optimization problem

// Define the optimization problem

var problem = OptimizationProblem(mesh)

// Add the area energy using the built-in Area functional

var area = Area()

problem.addenergy(area)

This is one of the simplest shape optimization problems, where our functional just contains one term: the area integral.

Now, we initialize our shape optimizer

// Define the optimizer

var opt = ShapeOptimizer(problem, mesh)

// Ask the optimizer to fix the boundary rings

opt.fix(bnd)

Note that we ask the optimizer to fix the boundary selection as desired. For the final step, we minimize the energy with respect to the shape. Currently, Morpho provides three different optimizatio routines --- a simple gradient descent with a fixed step size (relax), a gradient descent with a bracketing line-search (linesearch) and a conjugate gradient method (conjugategradient). Here, we will use the conjugategradient method and plot the resulting output.

// Minimize!

opt.conjugategradient(1000)


g = plotselection(mesh, bnd, grade=1)

g.title = "After"

Show(g)

Final mesh showing the catenoid

And we have our catenoid! We can confirm that the area has indeed decreased by printing area.total(mesh) before and after minimization. One can play around changing the separation-to-diameter ratio and observing the resulting shapes. The full code for this example can be found on GitHub here.