Flexible capacitor, part 4: Fixing the Shape

[latexpage]
In the last Flexible capacitor tutorial, we implemented refinement of the manifold to gradually get a better solution. One minor detail is that in refining the manifold, the refined boundary wasn’t circular. There are quite a few ways to fix this in Morpho, and one approach is described below. Begin by inserting the below code to your script, immediately before the “Main section” comment,

[code lang=”js” gutter=”false” collapse=”true” title=”Fixing the shape code”]
/* —————————————————————
* Reproject boundaries onto the unit circle using a listener
* ————————————————————— */

// Normalizes a vector given as a list by dividing throughout by the norm
normalize(list) = {
norm=sqrt(sum(list[i]^2, {i,length(list)}));
table(list[i]/norm, {i,length(list)})
}

// circularize
@circularizer = {
update(manifold, result) = {
bnd = manifold.selectboundary(); // Creates a selection with the boundary elements and vertices
ids = bnd.idlistforgrade(manifold, 0); // Obtains a list of vertex ids
do(
posn=manifold.vertexposition(ids[i]); // Gets the position of the vertex i
manifold.setvertexposition(ids[i], normalize(posn)) // normalizes the position and sets it
,{i,length(ids)}
);
m.changed(); // Tell the body that the manifold has changed to ensure the display is updated
}
}

// Instantiate a circularizer object
circ=new(circularizer);
// Have this object listen to the refine selector of the manifold
m[1].addlistener(circ, refine, update);
[/code]

Now rerun the script, and observe that the refined mesh stays circular!

How does this work? It uses some advanced Morpho features, so if you’re still fairly new feel free to skip the explanation and move on to the next tutorial.

For those still reading, notice that the main thing the code above does is to create a new class, circularizer, which implements a single method “update”. The line [code gutter=”false”]circ=new(circularizer);[/code] simply creates an object of this class.

The clever stuff happens on the line [code gutter=”false”]m[1].addlistener(circ, refine, update);[/code]

which attaches the circ object as a ‘listener’ to the manifold. A listener is an object-object relationship, so that whenever a chosen method of the target object is called, a specified method of the listening object is then called. In this case, what this means is that whenever the refine method of the manifold is called by any object or the user, the update method of the circ object is immediately called after. Listeners are always called with a reference to the target object as the first parameter, and the output of the target method as the second, hence update takes two arguments manifold and result.

It ought to be very clear that listeners are a powerful means of being automatically notified of changes in object state, and many Morpho internal classes use them: for example, the field class listens to manifolds to interpolate onto new vertices created during refinement. Listeners can be attached and detached at any time, and are automatically deleted when either the target or the listener are destroyed.

Now we’ve described the basic idea, the contents of update, which are carefully commented, should be more clear: when the circ object is notified that the manifold object has been refined, it selects the boundary elements, finds their ids and then simply reprojects them onto the unit circle using a function normalize.

As noted, listeners are a powerful but more advanced Morpho feature that you’ll likely only rarely need to use, but they provide elegant solutions to some problems. In the next tutorial, we’ll compare the solution we’ve now obtained to an analytical solution, testing the effectiveness of the adaptive refinement.

Leave a Reply