Commit 202b0506 authored by Dennis Gläser's avatar Dennis Gläser
Browse files

Merge branch 'feature/python-examples-docgen' into 'master'

[examples] Implement Python-only doc generator using pyparsing

See merge request !1914
parents b5bda6da 2a696d58
#!/bin/sh
# check if help is needed
if test "$1" = "--help" || test "$1" = "-help" \
|| test "$1" = "help" || test "$1" = ""; then
echo ""
echo "USAGE: $0 FILENAME"
echo ""
echo "The argument should be a C++ header or source file."
echo "The file is converted to Markdown and forwarded to stdout."
echo "In particular, a C++ comment is converted to its content,"
echo "while normal code is put into corresponding Markdown code blocks."
echo "Supposed to be called by the script merge_cpp_and_md.sh."
exit 0
fi
if [[ ${1: -3} == ".hh" ]]; then
sed '1,/#define/d' $1 >tmpfile
isheader=true
else
sed '1,/\*\*\*\*\*\//d' $1 >tmpfile
isheader=false
fi
insidecodeblock=false
lastline=""
strippedlastline=""
firstline=true
while IFS= read -r line
do
strippedline=$(echo $line | sed "s/^[ \t]*//")
if [[ $firstline == false ]]; then
if [[ ${strippedline:0:2} == "//" ]]; then
if [[ $insidecodeblock == true ]]; then
if [[ $lastline != "" ]]; then
echo "$lastline"
fi
echo "\`\`\`"
insidecodeblock=false
else
linetoprint=$(echo ${strippedlastline:2} | sed "s/^[ \t]*//")
echo "$linetoprint"
fi
else
if [[ $insidecodeblock == false ]]; then
linetoprint=$(echo ${strippedlastline:2} | sed "s/^[ \t]*//")
echo "$linetoprint"
echo "\`\`\`cpp"
insidecodeblock=true
else
echo "$lastline"
fi
fi
else
if [[ ${strippedline:0:2} != "//" && $line != "" ]]; then
echo "\`\`\`cpp"
insidecodeblock=true
fi
firstline=false
fi
lastline="$line"
strippedlastline="$strippedline"
done < tmpfile
if [[ $isheader == false ]]; then
if [[ ${strippedlastline:0:2} == "//" ]]; then
if [[ $insidecodeblock == true ]]; then
echo "\`\`\`"
insidecodeblock=false
fi
linetoprint=$(echo ${strippedlastline:2} | sed "s/^[ \t]*//")
echo "$linetoprint"
else
if [[ $insidecodeblock == false ]]; then
echo "\`\`\`cpp"
insidecodeblock=true
fi
echo "$lastline"
fi
fi
if [[ $insidecodeblock == true ]]; then
echo "\`\`\`"
insidecodeblock=false
fi
#!/bin/sh
# check if help is needed
if test "$1" = "--help" || test "$1" = "-help" \
|| test "$1" = "help" || test "$1" = ""; then
echo ""
echo "USAGE: $0 FILENAME_1 [FILENAME_2 ...]"
echo ""
echo "The arguments should be names of Markdown and/or C++ files."
echo "The script loops through all arguments. If a Markdown file is encountered,"
echo "its content is forwarded to stdout. For another file, a Markdown header"
echo "stating the filename is printed and the script cpp_to_md.sh is applied."
echo "To be used for creating README.mds in the example folder. For example, by"
echo "bash $0 intro.md spatialparams.hh problem.hh main.cc results.md >README.md"
exit 0
fi
for filename in "$@"
do
if [[ ${filename: -3} == ".md" ]]; then
cat $filename
else
echo ""
echo ""
echo "## The file \`$filename\`"
echo ""
bash cpp_to_md.sh $filename
echo ""
fi
done
doc/intro.md
spatialparams_1p.hh
problem_1p.hh
spatialparams_tracer.hh
problem_tracer.hh
main.cc
doc/results.md
{
"README.md" : [
"doc/intro.md",
"spatialparams_1p.hh",
"problem_1p.hh",
"spatialparams_tracer.hh",
"problem_tracer.hh",
"main.cc",
"doc/results.md"
]
}
This diff is collapsed.
......@@ -17,8 +17,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
// We look now at the main file for the tracer problem. We set up two problems in this file and solve them sequentially, first the 1p problem and afterwards the tracer problem. The result of the 1p problem is the pressure distribution in the problem domain. We use it to calculate the volume fluxes, which act as an input for the tracer problem. Based on this volume fluxes, we calculate the transport of a tracer in the following tracer problem.
// ### Includes
// ## Main program flow (`main.cc`)
//
//
// This file contains the main program flow. In this example, we solve a single-phase flow problem
// to obtain a pressure distribution on the domain. Subsequently, the distribution of volume fluxes
// is computed from that pressure distribution, which is then passed to a tracer problem to solve
// the transport of an initial contamination through the model domain.
// ### Included header files
#include <config.h>
// We include both problems in the main file, the `problem_1p.hh` and the `problem_tracer.hh`.
......
......@@ -17,11 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
// ### Header guard
#ifndef DUMUX_ONEP_TEST_PROBLEM_HH
#define DUMUX_ONEP_TEST_PROBLEM_HH
// ## The file `problem_1p.hh`
//
//
// Before we enter the problem class containing initial and boundary conditions, we include necessary files and introduce properties.
// ### Include files
// We use `YaspGrid`, an implementation of the dune grid interface for structured grids:
......@@ -130,7 +131,7 @@ public:
OnePTestProblem(std::shared_ptr<const GridGeometry> gridGeometry)
: ParentType(gridGeometry) {}
// First, we define the type of boundary conditions depending on the location. Two types of boundary conditions
// First, we define the type of boundary conditions depending on the location. Two types of boundary conditions
// can be specified: Dirichlet or Neumann boundary condition. On a Dirichlet boundary, the values of the
// primary variables need to be fixed. On a Neumann boundary condition, values for derivatives need to be fixed.
// Mixed boundary conditions (different types for different equations on the same boundary) are not accepted for
......@@ -154,7 +155,7 @@ public:
return values;
}
// Second, we specify the values for the Dirichlet boundaries. We need to fix values of our primary variable
// Second, we specify the values for the Dirichlet boundaries. We need to fix values of our primary variable
PrimaryVariables dirichlet(const Element &element,
const SubControlVolumeFace &scvf) const
{
......
......@@ -17,10 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
// ### Header guard
#ifndef DUMUX_TRACER_TEST_PROBLEM_HH
#define DUMUX_TRACER_TEST_PROBLEM_HH
// ## The file `problem_tracer.hh`
//
//
//Before we enter the problem class containing initial and boundary conditions, we include necessary files and introduce properties.
// ### Include files
// Again, we use YaspGrid, the implementation of the dune grid interface for structured grids:
......
......@@ -17,10 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
// the header guard
#ifndef DUMUX_INCOMPRESSIBLE_ONEP_TEST_SPATIAL_PARAMS_HH
#define DUMUX_INCOMPRESSIBLE_ONEP_TEST_SPATIAL_PARAMS_HH
// ## The file `spatialparams_1p.hh`
//
//
// In this file, we generate a random permeability field in the constructor of the `OnePTestSpatialParams` class.
// For this, we use the random number generation facilities provided by the C++ standard library.
#include <random>
......
......@@ -17,12 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
// the header guard
#ifndef DUMUX_TRACER_TEST_SPATIAL_PARAMS_HH
#define DUMUX_TRACER_TEST_SPATIAL_PARAMS_HH
// ## The file `spatialparams_tracer.hh`
//
//
// In this file, we define spatial properties of the porous medium such as the permeability and the porosity in various functions for the tracer problem.
// Furthermore, spatial dependent properties of the tracer fluid system are defined and in the end two functions handle the calculated volume fluxes from the solution of the 1p problem.
// We use the properties for porous medium flow models, declared in the file `properties.hh`.
......
doc/intro.md
spatialparams.hh
problem.hh
main.cc
doc/results.md
{
"README.md" : [
"doc/intro.md",
"spatialparams.hh",
"problem.hh",
"main.cc",
"doc/results.md"
]
}
This diff is collapsed.
......@@ -16,7 +16,9 @@
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
// ## The main file
// ## The file `main.cc`
//
//
// This is the main file for the 2pinfiltration example. Here we can see the programme sequence and how the system is solved using Newton's method
// ### Includes
#include <config.h>
......@@ -110,7 +112,7 @@ int main(int argc, char** argv) try
// In the problem, we define the boundary and initial conditions.
using Problem = GetPropType<TypeTag, Properties::Problem>;
auto problem = std::make_shared<Problem>(gridGeometry);
// We call the `computePointSourceMap` method to compute the point sources. The `computePointSourceMap` method is inherited from the fvproblem and therefore specified in the `dumux/common/fvproblem.hh`. It calls the `addPointSources` method specified in the `problem.hh` file.
// We call the `computePointSourceMap` method to compute the point sources. The `computePointSourceMap` method is inherited from the fvproblem and therefore specified in the `dumux/common/fvproblem.hh`. It calls the `addPointSources` method specified in the `problem.hh` file.
problem->computePointSourceMap();
// We initialize the solution vector,
......@@ -160,7 +162,7 @@ int main(int argc, char** argv) try
}
}
// Depending on the initial conditions, another grid adaptation might be necessary. The gridadaptindicator uses the input parameters `Adaptive.RefineTolerance` and `Adaptive.CoarsenTolerance` for this step.
// Depending on the initial conditions, another grid adaptation might be necessary. The gridadaptindicator uses the input parameters `Adaptive.RefineTolerance` and `Adaptive.CoarsenTolerance` for this step.
indicator.calculate(x, refineTol, coarsenTol);
//we mark the elements that were adapted
......
......@@ -17,13 +17,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
// ## Header guard
// The header guard (or include guard) prevents compilation errors due to duplicate definitions.
// Here, a unique name needs to be defined for the header file:
#ifndef DUMUX_LENSPROBLEM_POINTSOURCE_ADAPTIVE_HH
#define DUMUX_LENSPROBLEM_POINTSOURCE_ADAPTIVE_HH
// ## Include files
// ## The file `problem.hh`
//
//
// ### Include files
// The grid we use
#include <dune/alugrid/grid.hh>
......@@ -125,7 +125,7 @@ namespace Dumux {
//We leave the namespace Properties.
}
// ## The problem class
// ### The problem class
// We enter the problem class where all necessary boundary conditions and initial conditions are set for our simulation.
// As this is a porous medium problem, we inherit from the basic PorousMediumFlowProblem.
template <class TypeTag >
......
......@@ -17,10 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
// the header guard
#ifndef DUMUX_TWOP_INCOMPRESSIBLE_EXAMPLE_SPATIAL_PARAMS_HH
#define DUMUX_TWOP_INCOMPRESSIBLE_EXAMPLE_SPATIAL_PARAMS_HH
// ## The file `spatialparams.hh`
//
//
//we include the basic spatial parameters for finite volumes file from which we will inherit
#include <dumux/material/spatialparams/fv.hh>
......
#!/usr/bin/env python3
"""
A simple documentation generator
generating Markdown-documented examples
from annotated source code
"""
from pyparsing import *
# tell pyparsing to never ignore white space characters
ParserElement.setDefaultWhitespaceChars('')
def parseTaggedContent(keyname, action, open="[[", close="]]", endTag="/"):
"""
Match content between [[keyname]] and [[/keyname]] and apply the action on it
"""
start = LineStart() + ZeroOrMore(" ") + "//" + ZeroOrMore(" ") + (open + str(keyname) + close) + LineEnd()
end = LineStart() + ZeroOrMore(" ") + "//" + ZeroOrMore(" ") + (open + endTag + str(keyname) + close) + LineEnd()
return start.suppress() + SkipTo(end).setParseAction(action) + end.suppress()
def createMarkdownCode(markDownToken):
"""
Put code into Markdown syntax for code blocks with syntax highlighting
"""
def action(token):
# only print code snippets with content
if not token[0].rstrip():
return ""
else:
return "\n```" + markDownToken + "\n" + token[0].rstrip() + "\n```\n\n"
return action
def cppRules():
"""
Define a list of rules to apply for cpp source code
"""
suppressHeader = Suppress(Combine("// -*-" + SkipTo("*******/" + LineEnd(), include=True)))
suppressHeaderGuard = Suppress("#ifndef" + Optional(restOfLine) + LineEnd() + "#define" + Optional(restOfLine))
suppressEndHeaderGuard = Suppress("#endif" + Optional(restOfLine))
# make a code block (possibly containing comments) between [[codeblock]] and [[/codeblock]]
createCppBlock = createMarkdownCode("cpp")
parseCodeblock = parseTaggedContent("codeblock", action=createCppBlock)
# treat doc and code line
parseDoc = LineStart() + Suppress(ZeroOrMore(" ") + "//" + ZeroOrMore(" ")) + Optional(restOfLine)
parseCode = LineStart() + ~(ZeroOrMore(" ") + "//") + (SkipTo(parseDoc) | SkipTo(StringEnd()))
parseCode.setParseAction(createCppBlock)
docTransforms = parseCodeblock | parseDoc | parseCode
return [suppressHeader, suppressHeaderGuard, suppressEndHeaderGuard, docTransforms]
def transformCode(code, rules):
# exclude stuff between [[exclude]] and [[/exclude]]
exclude = parseTaggedContent("exclude", action=replaceWith(""))
code = exclude.transformString(code)
for transform in rules:
code = transform.transformString(code)
return code
doc/intro.md
problem.hh
main.cc
doc/results.md
{
"README.md" : [
"doc/intro.md",
"problem.hh",
"main.cc",
"doc/results.md"
]
}
......@@ -24,12 +24,12 @@ In the following, we take a close look at the files containing the set-up: At fi
## The file `problem.hh`
### The problem class
We enter the problem class where all necessary initial and boundary conditions are set for our simulation.
As this is a Stokes problem, we inherit from the basic <code>NavierStokesProblem</code>.
<details><summary>Toggle to expand code:</summary>
```cpp
#include <dumux/freeflow/navierstokes/problem.hh>
......@@ -40,12 +40,14 @@ template <class TypeTag>
class ChannelExampleProblem : public NavierStokesProblem<TypeTag>
{
```
</details>
We use convenient declarations that we derive from the property system.
<details>
<summary>Toggle to expand code (convenient declarations)</summary>
```cpp
using ParentType = NavierStokesProblem<TypeTag>;
using BoundaryTypes = GetPropType<TypeTag, Properties::BoundaryTypes>;
......@@ -62,6 +64,7 @@ We use convenient declarations that we derive from the property system.
public:
```
</details>
There follows the constructor of our problem class:
......@@ -70,6 +73,7 @@ As no run-time value is specified, we set the outlet pressure to 1.1e5 Pa.
<details>
<summary>Toggle to expand code (constructor)</summary>
```cpp
ChannelExampleProblem(std::shared_ptr<const GridGeometry> gridGeometry)
: ParentType(gridGeometry)
......@@ -78,6 +82,7 @@ As no run-time value is specified, we set the outlet pressure to 1.1e5 Pa.
outletPressure_ = getParam<Scalar>("Problem.OutletPressure", 1.1e5);
}
```
</details>
Now, we define the type of initial and boundary conditions depending on location.
......@@ -94,6 +99,7 @@ of our domain else.
<details>
<summary>Toggle to expand code (<code>boundaryTypesAtPos</code>)</summary>
```cpp
BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const
{
......@@ -117,6 +123,7 @@ of our domain else.
return values;
}
```
</details>
Second, we specify the values for the Dirichlet boundaries. We need to fix the values of our primary variables.
......@@ -125,6 +132,7 @@ in x-direction is set to zero if not at the inlet.
<details>
<summary>Toggle to expand code (<code>dirichletAtPos</code>)</summary>
```cpp
PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const
{
......@@ -138,6 +146,7 @@ in x-direction is set to zero if not at the inlet.
return values;
}
```
</details>
We specify the values for the initial conditions.
......@@ -145,6 +154,7 @@ We assign constant values for pressure and velocity components.
<details>
<summary>Toggle to expand code (<code>initialAtPos</code>)</summary>
```cpp
PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const
{
......@@ -157,6 +167,7 @@ We assign constant values for pressure and velocity components.
return values;
}
```
</details>
We need to specify a constant temperature for our isothermal problem.
......@@ -164,53 +175,61 @@ We set it to 10°C.
<details>
<summary>Toggle to expand code (<code>temperature</code>)</summary>
```cpp
Scalar temperature() const
{ return 273.15 + 10; }
private:
```
</details>
The inlet is at the left side of the physical domain.
<details>
<summary>Toggle to expand code (<code>isInlet_</code>)</summary>
```cpp
bool isInlet_(const GlobalPosition& globalPos) const
{
return globalPos[0] < eps_;
}
```
</details>
The outlet is at the right side of the physical domain.
<details>
<summary>Toggle to expand code (<code>isOutlet_</code>)</summary>
```cpp
bool isOutlet_(const GlobalPosition& globalPos) const
{
return globalPos[0] > this->gridGeometry().bBoxMax()[0] - eps_;
}
```
</details>
Finally, private variables are declared:
<details>
<summary>Toggle to expand code (private variables)</summary>
```cpp
static constexpr Scalar eps_=1e-6;
Scalar inletVelocity_;
Scalar outletPressure_;
};
}
#endif
```
</details>
## The file `main.cc`
......@@ -226,12 +245,14 @@ and another standard header for in- and output.
<details>
<summary>Toggle to expand code (includes of problem file and of standard headers)</summary>
```cpp
#include <config.h>
#include <ctime>
#include <iostream>
```
</details>
Dumux is based on DUNE, the Distributed and Unified Numerics Environment, which provides several grid managers and
......@@ -239,6 +260,7 @@ linear solvers. So we need some includes from that.
<details>
<summary>Toggle to expand code (dune includes)</summary>
```cpp
#include <dune/common/parallel/mpihelper.hh>
#include <dune/common/timer.hh>
......@@ -246,6 +268,7 @@ linear solvers. So we need some includes from that.
#include <dune/grid/io/file/vtk.hh>
#include <dune/istl/io.hh>
```
</details>
In Dumux, a property system is used to specify the model. For this, different properties are defined containing
......@@ -267,6 +290,7 @@ The following class contains functionality for additional flux output to the con
<details>
<summary>Toggle to expand code (dumux includes)</summary>
```cpp
#include <dumux/common/properties.hh>
#include <dumux/common/parameters.hh>
......@@ -290,6 +314,7 @@ The following class contains functionality for additional flux output to the con
#include "problem.hh"
```
</details>
</details>
......@@ -302,6 +327,7 @@ We setup the DuMux properties for our simulation (click [here](https://git.iws.u
4. The problem class `ChannelExampleProblem`, which is forward declared before we enter `namespace Dumux` and defined later in this file, is defined to be the problem used in this test problem (charaterized by the TypeTag `ChannelExample`). The fluid system, which contains information about the properties such as density, viscosity or diffusion coefficient of the fluid we're simulating, is set to a constant one phase liquid.
5. We enable caching for the following classes (which stores values that were already calculated for later usage and thus results in higher memory usage but improved CPU speed): the grid volume variables, the grid flux variables, the finite volume grid geometry.
```cpp
namespace Dumux::Properties {
......@@ -334,11 +360,13 @@ struct EnableGridGeometryCache<TypeTag, TTag::ChannelExample> { static constexpr
```
### Beginning of the main function
We begin the main function by making the type tag `ChannelExample`, that we defined in `problem.hh` for this test problem available here.
Then we initializing the message passing interface (MPI), even if we do not plan to run the application in parallel. Finalizing of the MPI is done automatically on exit.
We continue by printing the dumux start message and parsing the command line arguments and runtimeparameters from the input file in the init function.
```cpp
int main(int argc, char** argv) try
{
......@@ -354,6 +382,7 @@ int main(int argc, char** argv) try
Parameters::init(argc, argv);
```
### Set-up and solving of the problem