Add FEM Equation Tutorial

In this tutorial we are going to add the flow equation to FreeCAD and implement support for elmer solver. Please make sure you have read and understood Extend FEM Module before reading this tutorial.

The task can be split into four parts. The first step is to make the FEM workbench aware of a new type of equation. This step must only be done if the equation doesn't exist in FreeCAD jet (as opposed to a equation that is already in FreeCAD but not supported by the target solver). The secound step is to add a concrete document object representing the elmer specific equation. The third step is to add support for the new equation to the solver object of elmer. After that the analysis export of elmer must be extended to support the new equation type.

New Equation Type
In this step we are going to modify the following files:
 * src/Mod/Fem/FemSolver/EquationBase.py
 * src/Mod/Fem/PyGui/_CommandFemEquation.py
 * src/Mod/Fem/Workbench.cpp
 * src/Mod/Fem/Gui/Resources/Fem.qrc

The equation type is shared among all equation objects of the different solver. Each type has a string specifier (e.g. &quot;Heat&quot;) and a dedicated command that adds the equation to the selected solver. This allows for a simpler GUI where we have only one button for the heat equation which is used for all supported solver.

First add the new equation to the EquationBase module. Each equation requires two classes. A document proxy and a view proxy. Just copy-paste them from an existing equation type and adjust the icon path inside getIcon(self) of the view proxy. class FlowProxy(BaseProxy): pass

class FlowViewProxy(BaseViewProxy): def getIcon(self): return &quot;:/icons/fem-equation-flow.svg&quot;

Those two classes will later be used as base classes for the elmer specific equation classes. In addition to those base classes we have to create a new command class that adds a flow equation to the selected solver object. Additionally, the new .svg has to be registered with icons/fem-equation-flow.svg in Fem.qrc.

The command should be added to the _CommandFemEquation module. Just copy an existing command and adjust the icon, menu text and tool-tip in GetResources(self) and the specifier in getSpecifier(self). We will need the specifier again later in the tutorial. Don't forget to register the command at the bottom of the module file by using the addCommand(...) method. class Flow(_Base):

def getSpecifier(self): return &quot;Flow&quot;

def GetResources(self): return { 'Pixmap': 'fem-equation-flow', 'MenuText': &quot;Flow Equation&quot;, 'ToolTip': &quot;Add flow equation to selected solver.&quot; }

Gui.addCommand('FEM_AddEquationFlow', Flow)

Our newly created command still needs to be made accessable via the GUI of the FEM workbench. To add it to the toolbar search for the following code snippet in Workbench.cpp and add the new command to the rest of the equation commands. Gui::ToolBarItem* solve = new Gui::ToolBarItem(root); solve-&gt;setCommand(&quot;Solve&quot;); *solve &lt;&lt; &quot;FEM_SolverCalculix&quot; &lt;&lt; &quot;FEM_AddSolverElmer&quot; &lt;&lt; &quot;Separator&quot; &lt;&lt; &quot;FEM_AddEquationHeat&quot; &lt;&lt; &quot;FEM_AddEquationElasticity&quot; +     &lt;&lt; &quot;FEM_AddEquationFlow&quot; &lt;&lt; &quot;Separator&quot; &lt;&lt; &quot;FEM_SolverControl&quot; &lt;&lt; &quot;FEM_SolverRun&quot;;

We are also going to add the flow equation command to the solve menu of the FEM workbench. To do this insert our equation into the following code snippet in Workbench.cpp. Gui::MenuItem* solve = new Gui::MenuItem; root-&gt;insertItem(item, solve); solve-&gt;setCommand(&quot;&amp;Solve&quot;); *solve &lt;&lt; &quot;FEM_SolverCalculix&quot; &lt;&lt; &quot;FEM_SolverZ88&quot; &lt;&lt; &quot;FEM_AddSolverElmer&quot; &lt;&lt; &quot;Separator&quot; &lt;&lt; &quot;FEM_AddEquationHeat&quot; &lt;&lt; &quot;FEM_AddEquationElasticity&quot; +      &lt;&lt; &quot;FEM_AddEquationFlow&quot; &lt;&lt; &quot;Separator&quot; &lt;&lt; &quot;FEM_SolverControl&quot; &lt;&lt; &quot;FEM_SolverRun&quot;;

Elmers Equation Object
In this step we are going to modify the following files:
 * src/Mod/Fem/CMakeLists.txt
 * src/Mod/Fem/App/CMakeLists.txt

and add the following new file:
 * src/Mod/Fem/FemSolver/Elmer/Equations/Flow.py

Lets start with the module that implements the document object. In can be copied from an existing equation. If the new equation only supports keywords for linear systems copy the FemSolver/Elmer/Equations/Elasticity.py module. If it supports non-linear keywords too copy FemSolver/Elmer/Equations/Heat.py. The flow equation in elmer is a potentially non-linear equation. This means that we are going to base our work on Heat.py.

After copying Heat.py to Flow.py adjust - the name argument of the create module function, - the Type attribute of the Proxy class, - the base classes of the Proxy and the ViewProxy classes, - and the properties added via the obj.addProperty(..) function to those needed by the equation. def create(doc, name=&quot;Flow&quot;): return FemMisc.createObject(       doc, name, Proxy, ViewProxy)

class Proxy(Nonlinear.Proxy, FemEquation.FlowProxy): Type = &quot;Fem::FemEquationElmerFlow&quot; def __init__(self, obj): super(Proxy, self).__init__(obj) obj.Priority = 10

class ViewProxy(Nonlinear.ViewProxy, FemEquation.FlowViewProxy): pass

At the moment of writing this tutorial elmers flow equation doesn't have any special properties. See elmers elasticity equation for an example with properties.

Last but not least register the new module file (Flow.py) in both CMakeLists.txt files the way descripted in Extend FEM Module. The suitable lists can be easily found by searching for existing equation modules files of elmer.

Extend Solver Object
In this step we are going to modify the following file:
 * src/Mod/Fem/FemSolver/Elmer/Object.py

Right now we made FreeCAD aware that there is a new type of equation and even added a command that adds this equation to the selected solver object. We also implemented a concrete equation object for elmer. Whats left to do now it to make the connection beween elmer and the flow equation. This must be done directely in elmers solver object.

Register the module in which we just implemented our new equation object (Flow.py) with the equation specifier from step 1 (&quot;Flow&quot;) in the Proxy._EQUATIONS list in Elmer/Object.py. _EQUATIONS = { &quot;Heat&quot;: Equations.Heat, &quot;Elasticity&quot;: Equations.Elasticity, +   &quot;Flow&quot;: Equations.Flow, }

Extend Analysis Export
In this step we are going to modify the following file:
 * src/Mod/Fem/FemSolver/Elmer/Writer.py

This is the most demaning part of implementing a new equation. This file contains a Writer class which exports the analysis into elmers sif format.

For every supported equation there are a series of methods handling the export of the respective equation. Just copy all of them from an existing equation and adjust them to your needs. Our Flow equation uses the following methods:
 * _handleFlow
 * _getFlowSolver
 * _handleFlowConstants
 * _handleFlowBndConditions
 * _handleFlowInitial
 * _handleFlowBodyForces
 * _handleFlowMaterial
 * _handleFlowEquation