\[\newcommand{\euclid}[1]{\mathbb{R}^{#1}} \newcommand{\pos}{\boldsymbol{x}} \newcommand{\field}{u} \newcommand{\source}{g} \newcommand{\body}{\Omega} \newcommand{\surf}{\partial\body} \newcommand{\grad}[1]{\boldsymbol{\nabla}(#1)} \newcommand{\tline}{[t_0, t_{\infty}]}\]

Library description

Main classes

BVP

The bvp class is the main class of the library.

It features a minimalistic signature: bvp(domain, vform, bc) that matches exactly the three main elements on which the formal definition of a boundary-value problem relies on. The three corresponding arguments self.domain, self.vform and self.bnd_conds are set during initiation. They can be adjusted afterwards with specific setters, respectively: self.set_domain(), self.set_vform() and self.add_boundary_condition().

The self.set_vform() method, called within self.__init__(), instanciates the functionspace bases on the mesh specified within the domain argument and the finite element characteristics set within the vform argument.

The central method of the bvp class is the solve() one. This method instanciates the vform.lhs and vform.rhs, set the FEniCS solver to use depending on the variational form at stake and runs the simulation. If the considered vform has a vanishing rhs term, a non-linear solver will be chosen ; otherwize, a linear one will be picked.

Once bvp.solve() is called, the argument bvp.solution, initially set to None, is updated with the fenics.function computed solution.

The bvp.solution can then be visualized with the plot() function of the bvp.utils.visu module (c.f. description of this module hereafter). A self.info() method gathers and displays all the parameters of the simulation. This method makes use of the domain.info() and vform.info() ones. To performed more in-depth analysis of the simulation results, the whole problem — with its solution — can be saved on disk in the .xdmf format with the self.save_solution() method. The genericity of the .xdmf format enables then the analysis of the results with powerful softwares such as Paraview <https://www.paraview.org>.

IBVP

The ibvp class inherits directly from the bvp one. Its signature features two additional arguments: scheme and initial_state.

The former corresponds to the time-integration scheme to use. It must corresponds to a daughter class of the FirstOrderScheme class within the bvpy.solvers.time_integration module. Currently, the bvpy library features two such classes ImplicitEuler and ExplicitEuler.

The latter corresponds to the initial value from which the solution will be evolving. It must be given as a fenics.function. Note that the self.initial_state argument can be set after the ibvp initialization with the ibvp.set_initial_solution() method.

The main method of the ibvp class is self.integrate() that handles the time-integration loop. During each iteration of the loop, it performs two tasks:

  • Run a spatial resolution of the problem at the current time as a classic BVP. This is performed by the self.step() method that calls the self.solve() method inherited from the bvp mother class.

  • Record the solution of this spatial resolution as a “layer” in a temporal stack recorded as an .xdmf file.

Modules

The purpose of the first four modules listed below is to provide the library with dedicated tools to parametrize the previously described classes (bvp and ibvp). The fifth one (bvpy.utils) provides useful functions to handle the simulations, e.g. recording, plotting, etc…

Domains

The bvpy.domains module contains the classes that can be used to implement the notion of integration domain, \(\body\) in eq.(1). It is built around the (abstract) mother class AbstractDomain. All domain classes used as arguments for BVPs/IBVPs inherits from it. The AbstractDomain encompasses two complementary description of the integration domain:

  • A gmsh.model called the geometry of the domain and set by the geometry() abstract method.

  • A fenics.mesh called the mesh of the domain and produced by the discretize() method. Note that this method is not automatically called at instanciation but either by high-end functions such as plot() (from the bvpy.utils.visu module) or bvp.solve().

The notion of geometry and the related geometry() method are of paramount importance, for each domain class corresponds to a specific implementation of the geometry() method:

  • The classes contained within the bvpy.domains.primitives sub-module are built around geometry() methods that construct basic geometric elements (such as rectangles, disk, spheres…) exclusively with Gmsh tools.

Note

Basic operators such as __add__(), __sub__() and __and__() have been surcharged so primitives can be combined together to form more complex domains.

  • The CustomPolygonalDomain class (contained within the bvpy.domains.custom_polygonal sub-module) is built around a geometry() method that construct piecewise polygonal structures from lists of oriented points. This is also done thanks to Gmsh tools. The purpose of this class is to enable the use of cellularized structures as integration domains. This is an important feature in the context of modeling biological tissues and other multicellular continua.

  • Finally, the CustomDomain class (within the bvpy.domains.custom_domain sub-module) features an empty geometry() method for its purpose is to define a domain from an already existing mesh, sorted as a .ply-formatted file.

Besides a geometry and a mesh, domain classes also encompasses fenics.Measures elements defined within the domain (self.dx) and onto its border (self.ds) and some setter methods to parametrize

Note

Domain examples are given in the following tutorial: domain examples<bvpy_tutorial_domain>

Vforms

The goal of the bvpy.vforms module is to gather weak formulations of differential equations and systems that can be implemented in Bvpy. The corresponding vform classes constitute the second mandatory argument of the bvp and ibvp class signatures. Similarly to the bvpy.domains module, the bvpy.vforms module is built around one main (abstract) mother class AbstractVform, other vform classes contained within sub-modules inherit directly from it.

The heart of the AbstractVform class is the contruct_form() abstract method. Its purpose is to instantiate, in FEniCS, the bilinear (fenics.lhs) and the linear (fenics.rhs) operators that constitute the variational form of the differential equation (or system) at stake. In that matter, this method has to be written de novo each time a new (set of) differential equation(s) is to be considered.

In the current version of Bvpy, the resulting daughter classes of AbstractVform are compiled in field-specific sub-modules :

  • bvpy.vforms.poisson contains the simplest vform class, the one that encodes the scalar Poisson equation, which stands as the “Hello world !” for numerical analysis. This vform class encompasses equations of the general form:

\[\begin{equation} \Delta \phi(\mathbf{x}) + f(\mathbf{x}) = 0 \end{equation}\]
  • bvpy.vforms.helmholtz contains an instanciation of the helmholtz’ equation, namely Poisson’s equation augmented with a linear term:

\[\begin{equation} \Delta \phi(\mathbf{x}) +\alpha \phi(\mathbf{x}) + f(\mathbf{x}) = 0 \end{equation}\]
  • bvpy.vforms.transport contains two classes dedicated to transport equations: TransportForm and CoupledTransportForm. The former enables the study of equations of the general form:

\[ \begin{align}\begin{aligned}\newcommand{\grad}[1]{\boldsymbol{\nabla}(#1)}\\\grad{D(\mathbf{x})\grad{\phi}} + f(\mathbf{x}, \phi(\mathbf(x))) = 0,\end{aligned}\end{align} \]

where the function \(f(\mathbf{x}, \phi(\mathbf{x}))\) is non-linear. This general form has been thought to encompass active transport equations and reaction-diffusion ones.

The second class of sub-module specifically tackles the problem differential systems composed of several interdependent equations of the general form given above. Such system, can be generally written as:

\[\begin{split}\begin{cases} \grad{D_{\phi}(\mathbf{x})\grad{\phi}} + f_{\phi}(\mathbf{x}, \phi(\mathbf(x)), \psi(\mathbf(x))) = 0 \\ \grad{D_{\psi}(\mathbf{x})\grad{\psi}} + f_{\psi}(\mathbf{x}, \phi(\mathbf(x)), \psi(\mathbf(x))) = 0 \\ \dots \end{cases}\end{split}\]
  • bvpy.vforms.elasticity encompasses two vform classes: LinearElasticForm and HyperElasticForm implementing respectively linear and hyper-elastic equations. The former encompasses equations of the general form:

\[ \begin{align}\begin{aligned}\newcommand{\div}[1]{\boldsymbol{\nabla}\cdot #1} \newcommand{\stress}{\boldsymbol{\sigma}} \newcommand{\strain}{\boldsymbol{\varepsilon}} \newcommand{\stiffness}{\mathbb{H}} \newcommand{\grad}[1]{\boldsymbol{\nabla}(#1)}\\\begin{split}\begin{cases} \div{\stress} + \mathbf{f} = \mathbf{0} \\ \stress = \stiffness \colon \strain \\ \strain = \text{sym}\big(\grad{\mathbf{u}} \big) \end{cases}\end{split}\end{aligned}\end{align} \]

where \(\mathbf{u}\) stands for the displacement field. The tensor $stiffness$ accounts for the linear stiffness tensor of the considered material.

The latter encompasses hyper-elastic equations, i.e. valid beyond the linear deformation regime. They corresponds to the same general mathematical definition given above but with another strain definition:

\[ \begin{align}\begin{aligned}\newcommand{\idx}{\mathbf{I}_{\text{d}}} \newcommand{\grad}[1]{\boldsymbol{\nabla}(#1)}\\\strain = \frac{1}{2}\Big(\grad{u}^t\cdot\grad{u} - \idx \Big).\end{aligned}\end{align} \]

Boundary_conditions

The bvpy.boundary_conditions enables an easy implementation of boundary conditions through the use of the dirichlet function. This function takes two mandatory arguments : val and boundary. They respectively correspond to the value that one wants to impose on a boundary.

The val argument can have several types:

  • int ,``float``, list, tuple, numpy.ndarray . These types will encode constant dirichlet conditions, i.e. a (list of) constant value(s) on a given (list of) subset(s) of the border.

  • Non-constant dirichlet conditions will be encoded with val arguments given as strings.

The boundary argument must be an instance of the Boundary class defined within the bvpy.boundary_conditions.boundary module. This class enables the definition of the boundary or a subset of it from a string expression.

Solvers

The bvpy.solvers module gathers two types of finite element solvers: LinearSolver and NonLinearSolver. They are not ment to be set by the user directly. The choice between both types is made automatically by the bvp class depending on the variational form at stake. More precisely, if the instanced vform features a non-zero rhs element a NonLinearSolver will automatically instanced.

Besides the finite element solvers, the bvpy.solvers module also feature a time_integration sub-module that defines, in the current version, two time-integration schemes: ImplicitEuler and ExplicitEuler.

Utils

The bvpy.utils module can be seen as an “house-keeping” module that gathers various useful functions and classes regrouped in thematic sub-modules.

  • bvpy.utils.io: Read and write methods.

  • bvpy.utils.post_processing:

  • bvpy.utils.pre_processing:

  • bvpy.utils.visu :