0% found this document useful (0 votes)
76 views1,122 pages

Sfepy Manual

Uploaded by

Sin Mercy
Copyright
Β© Β© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
76 views1,122 pages

Sfepy Manual

Uploaded by

Sin Mercy
Copyright
Β© Β© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1122

SfePy Documentation

Release version: 2022.1+git.f9873484

Robert Cimrman and Contributors

May 03, 2022


CONTENTS

1 Documentation 3
1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 Supported Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.2 Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.3 Installing SfePy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2.4 Using SfePy Docker Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2.5 Installing SfePy from Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.6 Testing Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.7 Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.8 Using IPython . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.9 Notes on Multi-platform Python Distributions . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.10 Notes on Installing SfePy Dependencies on Various Platforms . . . . . . . . . . . . . . . . 9
1.3 Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.1 Basic SfePy Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.2 Basic Notions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3.3 Running a Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3.4 Example Problem Description File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3.5 Interactive Example: Linear Elasticity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.4 User’s Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.4.1 Running a Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.4.2 Visualization of Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.4.3 Problem Description File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.4.4 Building Equations in SfePy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
1.4.5 Term Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
1.4.6 Solution Postprocessing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1.4.7 Probing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
1.4.8 Postprocessing filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
1.4.9 Solvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
1.4.10 Solving Problems in Parallel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
1.4.11 Isogeometric Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
1.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
1.5.1 Primer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
1.5.2 Using Salome with SfePy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.5.3 Preprocessing: FreeCAD/OpenSCAD + Gmsh . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.5.4 Material Identification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
1.5.5 Mesh parametrization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
1.5.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
1.5.7 Example Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
1.6 Useful Code Snippets and FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586

i
1.6.1 Miscellaneous . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
1.6.2 Mesh-Related Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
1.6.3 Regions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
1.6.4 Material Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
1.7 Theoretical Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591
1.7.1 Notes on solving PDEs by the Finite Element Method . . . . . . . . . . . . . . . . . . . . . 591
1.7.2 Implementation of Essential Boundary Conditions . . . . . . . . . . . . . . . . . . . . . . . 593
1.8 Term Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595
1.8.1 Term Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595
1.8.2 Term Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596

2 Development 621
2.1 General Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621
2.2 Possible Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621
2.3 Developer Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
2.3.1 Retrieving the Latest Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
2.3.2 SfePy Directory Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
2.3.3 Exploring the Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
2.3.4 How to Contribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
2.3.5 How to Regenerate Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
2.3.6 How to Implement a New Term . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
2.3.7 Multi-linear Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
2.3.8 How To Make a Release . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
2.3.9 Module Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640

Bibliography 1033

Python Module Index 1035

Index 1039

ii
SfePy Documentation, Release version: 2022.1+git.f9873484

β€’ genindex
β€’ modindex
β€’ search

CONTENTS 1
SfePy Documentation, Release version: 2022.1+git.f9873484

2 CONTENTS
CHAPTER

ONE

DOCUMENTATION

1.1 Introduction

SfePy (http://sfepy.org) is a software for solving systems of coupled partial differential equations (PDEs) by the finite
element method in 1D, 2D and 3D. It can be viewed both as black-box PDE solver, and as a Python package which can
be used for building custom applications. The word β€œsimple” means that complex FEM problems can be coded very
easily and rapidly.
There is also a preliminary support for the isogeometric analysis, outlined in Isogeometric Analysis.
The code is written almost entirely in Python, with exception of the most time demanding routines - those are written
in C and wrapped by Cython or written directly in Cython.
SfePy is a free software released under the New BSD License. It relies on NumPy and SciPy (an excellent collection
of tools for scientific computations in Python). It is a multi-platform software that should work on Linux, Mac OS X
and Windows.
SfePy was originally developed as a flexible framework to quickly implement and test the mathematical models devel-
oped during our various research projects. It has evolved, however, to a rather full-featured (yet small) finite element
code. Many terms have been implemented that can be used to build the PDEs, see Term Overview. SfePy comes also
with a number of examples that can get you started, check Examples, Gallery and Tutorial. Some more advanced
features are discussed in Primer.

1.2 Installation

1.2.1 Supported Platforms

SfePy is known to work on various flavors of recent Linux, Intel-based MacOS and Windows. SfePy requires Python
3. The release 2019.4 was the last with Python 2.7 support.
Note: Depending on Python installation and OS used, replacing python by python3 might be required in all the
commands below (e.g. in Compilation of C Extension Modules) in order to use Python 3.

3
SfePy Documentation, Release version: 2022.1+git.f9873484

1.2.2 Requirements

Installation prerequisites, required to build SfePy:


β€’ a C compiler suite,
β€’ Python 3.x,
β€’ NumPy,
β€’ Cython.
Python packages required for using SfePy:
β€’ Pyparsing,
β€’ SciPy,
β€’ meshio for reading and writing mesh files,
β€’ scikit-umfpack for enabling UMFPACK solver for SciPy >= 0.14.0,
β€’ Matplotlib for various plots, GTKAgg for live plotting via log.py,
β€’ PyTables for storing results in HDF5 files,
β€’ SymPy for some tests and functions,
β€’ Mayavi for postproc.py,
β€’ igakit for script/gen_iga_patch.py - simple IGA domain generator,
β€’ petsc4py and mpi4py for running parallel examples and using parallel solvers from PETSc,
β€’ slepc4py for eigenvalue problem solvers from SLEPc
β€’ pymetis for mesh partitioning using Metis,
β€’ wxPython for better IPython integration.
β€’ Read the Docs Sphinx theme for building documentation
β€’ psutil for memory requirements checking
β€’ PyVista for post-processing via resview.py
Make sure the dependencies of those packages are also installed (e.g igakit reguires FORTRAN compiler, scikit-
umfpack does not work without UMFPACK, petsc4py without PETSc etc.). All dependencies of meshio need to be
installed for full mesh file format support (when using pip: pip install meshio[all]).
SfePy should work both with bleeding edge (Git) and last released versions of NumPy and SciPy. Please, submit an
issue at Issues page in case this does not hold.
Other dependencies/suggestions:
β€’ To be able to (re)generate the documentation Sphinx, numpydoc and LaTeX are needed (see How to Regenerate
Documentation).
β€’ If doxygen is installed, the documentation of data structures and functions can be automatically generated by
running:

python setup.py doxygendocs

β€’ Mesh generation tools use pexpect and gmsh or tetgen.


β€’ IPython is recommended over the regular Python shell to fluently follow some parts of primer/tutorial (see Using
IPython).

4 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

β€’ MUMPS library for using MUMPS linear direct solver (real and complex arithmetic, parallel factorization)

Notes on selecting Python Distribution

SfePy should work with any recent Python 3.x (in long-term view Python 3.6+ is recommended). It is only matter of
taste to use either native OS Python installation or any other suitable distribution. We could recommend the following
distributions to use:
β€’ Linux: OS native installation (See Notes on Installing SfePy Dependencies on Various Platforms for further
details.)
β€’ macOS: multi-platform scientific Python distributions Anaconda (See Notes on Multi-platform Python Distri-
butions for further details.)
β€’ Windows: use free versions of commercial multi-platform scientific Python distributions Anaconda or Enthought
Canopy (see Notes on Multi-platform Python Distributions for further details). In addition a completely free
open-source portable distribution WinPython can be used.
On any supported platform we could recommend Anaconda distribution as easy-to-use, stable and up-to-date Python
distribution with all the required dependencies (including pre-built sfepy package).
Note: all SfePy releases are regularly tested on recent Linux distributions (Debian and (K)Ubuntu) using OS Python
installation and Anaconda, macOS 10.12+ using Anaconda and Windows 8.1+ using Anaconda.

1.2.3 Installing SfePy

For Anaconda and .deb based Linux distributions (Debian, (K)Ubuntu), pre-built SfePy packages are available. You
may directly install them with:
β€’ Anaconda distribution: install sfepy from conda-forge channel:

conda install -c conda-forge sfepy

β€’ Debian/(K)Ubuntu: install python-sfepy:

sudo apt-get install python-sfepy

There are no further steps required to install/configure SfePy (see Notes on Multi-platform Python Distributions for
additional notes).

1.2.4 Using SfePy Docker Images

Besides the classical installation we also provide experimental Docker images with ready-to-run Anaconda and Sfepy
installation.
Before you start using SfePy images, you need to first install and configure Docker on your computer. To do this follow
official Docker documentation.
Currently available images are:
β€’ sfepy/sfepy-notebook - basic command line interface and web browser access to Jupyter notebook/JupyterLab
interface,
β€’ sfepy/sfepy-x11vnc-desktop - optimized Ubuntu desktop environment accessible via standard web browser or
VNC client.

1.2. Installation 5
SfePy Documentation, Release version: 2022.1+git.f9873484

For available runtime options and further information see sfepy-docker project on Github.
As a convenience, use the following Docker compose file, which will start the SfePy image, run Jupyter Lab, and map
the contents of the local directory to the SfePy home directory within the image. Just create an empty folder and add
the following to a file named docker-compose.yml. Then, run docker-compose up in the same directory.

1.2.5 Installing SfePy from Sources

The latest stable release can be obtained from the download page. Otherwise, download the development version of
the code from SfePy git repository:

git clone git://github.com/sfepy/sfepy.git

In case you wish to use a specific release instead of the latest master version, use:

git tag -l

to see the available releases - the release tags have form release_<year>.<int>.
See the download page for additional download options.

Compilation of C Extension Modules

In the SfePy top-level directory:


1. Look at site_cfg_template.py and follow the instructions therein. Usually no changes are necessary.
2. Compile the extension modules
β€’ for in-place use:

python setup.py build_ext --inplace

β€’ for installation:

python setup.py build

We recommend starting with the in-place build.

Installation

SfePy can be used without any installation by running its main scripts and examples from the top-level directory of the
distribution or can be installed locally or system-wide:
β€’ system-wide (may require root privileges):

python setup.py install

β€’ local (requires write access to <installation prefix>):

python setup.py install --root=<installation prefix>

If all went well, proceed with Testing Installation.

6 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

1.2.6 Testing Installation

After building and/or installing SfePy you should check if all the functions are working properly by running the auto-
mated tests.

Running Automated Test Suite

The tests can be run using:

python -c "import sfepy; sfepy.test()"

in the SfePy top-level directory in case of the in-place build and anywhere else when testing the installed package.
The testing function is based on pytest. Additional pytest options can be passed as arguments to sfepy.test(), for
example:

python -c "import sfepy; sfepy.test('-v', '--durations=0', '-m not slow')"

The tests output directory can be specified using:

python -c "import sfepy; sfepy.test('--output-dir=output-tests')"

1.2.7 Debugging

If something goes wrong, edit the site_cfg.py config file and set debug_flags = '-DDEBUG_FMF' to turn on
bound checks in the low level C functions, and recompile the code:

python setup.py clean


python setup.py build_ext --inplace

Then re-run your code and report the output to the SfePy mailing list.

1.2.8 Using IPython

We generally recommend to use (a customized) IPython interactive shell over the regular Python interpreter when
following Tutorial or Primer (or even for any regular interactive work with SfePy).
Install IPython (as a generic part of your selected distribution) and then customize it to your choice.
Depending on your IPython usage, you can customize your default profile or create a SfePy specific new one as follows:
1. Create a new SfePy profile:

ipython profile create sfepy

2. Open the ~/.ipython/profile_sfepy/ipython_config.py file in a text editor and add/edit after the c =
get_config() line:

exec_lines = [
'import numpy as nm',
'import matplotlib as mpl',
'mpl.use("WXAgg")',
#
# Add your preferred SfePy customization here...
(continues on next page)

1.2. Installation 7
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


#
]

c.InteractiveShellApp.exec_lines = exec_lines
c.TerminalIPythonApp.gui = 'wx'
c.TerminalInteractiveShell.colors = 'Linux' # NoColor, Linux, or LightBG

Please note, that generally it is not recommended to use star (*) imports here.
3. Run the customized IPython shell:

ipython --profile=sfepy

1.2.9 Notes on Multi-platform Python Distributions

Anaconda

We highly recommend this scientific-oriented Python distribution.


(Currently regularly tested by developers on SfePy releases with Python 3.6 64-bit on Ubuntu 16.04 LTS, Windows
8.1+ and macOS 10.12+.)
Download appropriate Anaconda Python 3.x installer package and follow install instructions. We recommend to choose
user-level install option (no admin privileges required).
Anaconda can be used for:
1. installing the latest release of SfePy directly from the conda-forge channel (see sfepy-feedstock). In this case,
follow the instructions in Installing SfePy.
Installing/upgrading SfePy from the conda-forge channel can also be achieved by adding conda-forge to your
channels with:

conda config --add channels conda-forge

Once the conda-forge channel has been enabled, SfePy can be installed with:

conda install sfepy

It is possible to list all of the versions of SfePy available on your platform with:

conda search sfepy --channel conda-forge

2. installing the SfePy dependencies only - then proceed with the Installing SfePy from Sources instructions.
In this case, install the missing/required packages using built-in conda package manager:

conda install mayavi wxpython

See conda help for further information.


Occasionally, you should check for distribution and/or installed packages updates (there is no built-in automatic update
mechanism available):

8 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

conda update conda


conda update anaconda
conda update <package>

or try:

conda update --all

Compilation of C Extension Modules on Windows

To build SfePy extension modules, included mingw-w32/64 compiler tools should work fine. If you encounter any
problems, we recommend to install and use Microsoft Visual C++ Build Tools instead (see Anaconda FAQ).

1.2.10 Notes on Installing SfePy Dependencies on Various Platforms

The following information has been provided by users of the listed platforms and may become obsolete over time. The
generic installation instructions above should work in any case, provided the required dependencies are installed.

Gentoo

emerge -va pytables pyparsing numpy scipy matplotlib ipython mayavi

Archlinux

pacman -S python2-numpy python2-scipy python2-matplotlib ipython2 python2-sympy


yaourt -S python-pytables python2-mayavi

Instructions

Edit Makefile and change all references from python to python2. Edit scripts and change shebangs to point to python2.

Debian

(Old instructions, check also (K)Ubuntu below.)


First, you have to install the dependencies packages:

apt-get install python-tables python-pyparsing python-matplotlib python-scipy

Than SfePy can be installed with:

apt-get install python-sfepy

1.2. Installation 9
SfePy Documentation, Release version: 2022.1+git.f9873484

(K)Ubuntu

(Tested on Kubuntu 16.04 LTS.)


First, you have to install the dependencies packages (if apt-get is not installed, install it or try apt-get install instead):

sudo apt-get install python-scipy python-matplotlib python-tables python-pyparsing␣


Λ“β†’libsuitesparse-dev python-setuptools mayavi2 python-dev ipython python-sympy cython␣

Λ“β†’python-sparse

Than SfePy can be installed with:

apt-get install python-sfepy

1.3 Tutorial

1.3.1 Basic SfePy Usage

SfePy package can be used in two basic ways as a:


1. Black-box Partial Differential Equation (PDE) solver,
2. Python package to build custom applications involving solving PDEs by the Finite Element Method (FEM).
This tutorial focuses on the first way and introduces the basic concepts and nomenclature used in the following parts
of the documentation. Check also the Primer which focuses on a particular problem in detail.
Users not familiar with the finite element method should start with the Notes on solving PDEs by the Finite Element
Method.

Invoking SfePy from the Command Line

This section introduces the basics of running SfePy from the command line.
The script simple.py is the most basic starting point in SfePy. It can be invoked in many (similar) ways which de-
pends on used OS, Python distribution and SfePy build method (see Installing SfePy for further info). All (working)
alternatives described below are interchangeable, so don’t panic and feel free to pick your preferred choice (see Basic
Usage for further explanation and more usage examples).
Depending on selected build method and OS used we recommend for:
β€’ In-place build
Use the top-level directory of SfePy source tree as your working directory and use:

./simple.py <problem_description_file>

or (particularly on Windows based systems)

python ./simple.py <problem_description_file>

β€’ Installed (local or system-wide) build


Use any working directory including your problem description file and use:

python <path/to/installed/simple.py> <problem_description_file>

10 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

or simply (on Unix based systems)

<path/to/installed/simple.py> <problem_description_file>

You can also use the simple SfePy command-wrapper (ensure that SfePy installation executable directory is
included in your PATH):

sfepy-run simple <problem_description_file>

Please note, that improper mixing of in-place and install builds on single command line may result in strange runtime
errors.

Using SfePy Interactively

All functions of SfePy package can be also used interactively (see Interactive Example: Linear Elasticity for instance).
We recommend to use the IPython interactive shell for the best fluent user experience. You can customize your IPython
startup profile as described in Using IPython.

1.3.2 Basic Notions

The simplest way of using SfePy is to solve a system of PDEs defined in a problem description file, also referred to
as input file. In such a file, the problem is described using several keywords that allow one to define the equations,
variables, finite element approximations, solvers and solution domain and subdomains (see Problem Description File
for a full list of those keywords).
The syntax of the problem description file is very simple yet powerful, as the file itself is just a regular Python module
that can be normally imported – no special parsing is necessary. The keywords mentioned above are regular Python
variables (usually of the dict type) with special names.
Below we show:
β€’ how to solve a problem given by a problem description file, and
β€’ explain the elements of the file on several examples.
But let us begin with a slight detour. . .

Sneak Peek: What is Going on Under the Hood

1. A top-level script (usually simple.py as in this tutorial) reads in an input file.


2. Following the contents of the input file, a Problem instance is created – this is the input file coming to life. Let
us call the instance problem.
β€’ The Problem instance sets up its domain, regions (various sub-domains), fields (the FE approximations),
the equations and the solvers. The equations determine the materials and variables in use – only those are
fully instantiated, so the input file can safely contain definitions of items that are not used actually.
3. The solution is then obtained by calling problem.solve() function, which in turn calls a top-level time-stepping
solver. In each step, problem.time_update() is called to setup boundary conditions, material parameters and
other potentially time-dependent data. The problem.save_state() is called at the end of each time step to save the
results. This holds also for stationary problems with a single β€œtime step”.
So that is it – using the code a black-box PDE solver shields the user from having to create the Problem instance by
hand. But note that this is possible, and often necessary when the flexibility of the default solvers is not enough. At the

1.3. Tutorial 11
SfePy Documentation, Release version: 2022.1+git.f9873484

end of the tutorial an example demonstrating the interactive creation of the Problem instance is shown, see Interactive
Example: Linear Elasticity.
Now let us continue with running a simulation.

1.3.3 Running a Simulation

The following commands should be run in the top-level directory of the SfePy source tree after compiling the C extension
files. See Installation for full installation instructions.
β€’ Download sfepy/examples/diffusion/poisson_short_syntax.py. It represents our sample SfePy Prob-
lem Description File, which defines the problem to be solved in terms SfePy can understand.
β€’ Use the downloaded file in place of <problem_description_file.py> and run simple.py as described above. The
successful execution of the command creates output file cylinder.vtk in the SfePy top-level directory.

Postprocessing the Results

β€’ The postproc.py script can be used for quick postprocessing and visualization of the SfePy output files. It requires
mayavi installed on your system.
β€’ As a simple example, try:

./postproc.py cylinder.vtk

β€’ The following interactive 3D window should display:

β€’ You can manipulate displayed image using:


– the left mouse button by itself orbits the 3D view,

12 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

– holding shift and the left mouse button pans the view,
– holding control and the left mouse button rotates about the screen normal axis,
– the right mouse button controls the zoom.

1.3.4 Example Problem Description File

Here we discuss the contents of the sfepy/examples/diffusion/poisson_short_syntax.py problem descrip-


tion file. For additional examples, see the problem description files in the sfepy/examples/ directory of SfePy.
The problem at hand is the following:

π‘βˆ†π‘‡ = 𝑓 in Ω, 𝑇 (𝑑) = 𝑇¯(𝑑) on Ξ“ , (1.1)

where Ξ“ βŠ† Ω is a subset of the domain Ω boundary. For simplicity, we set 𝑓 ≑ 0, but we still work with the material
constant 𝑐 even though it has no influence on the solution in this case. We also assume zero fluxes over πœ•β„¦ βˆ– Ξ“, i.e.
πœ•π‘› = 0 there. The particular boundary conditions used below are 𝑇 = 2 on the left side of the cylindrical domain
πœ•π‘‡

depicted in the previous section and 𝑇 = βˆ’2 on the right side.


The first step to do is to write (1.1) in weak formulation (1.15). The 𝑓 = 0, 𝑔 = πœ•π‘‡
πœ•π‘› = 0. So only one term in weak
form (1.15) remains:
∫︁
𝑐 βˆ‡π‘‡ Β· βˆ‡π‘  = 0, βˆ€π‘  ∈ 𝑉0 . (1.2)
Ξ©

Comparing the above integral term with the long table in Term Overview, we can see that SfePy contains this term
under name dw_laplace. We are now ready to proceed to the actual problem definition.
Open the sfepy/examples/diffusion/poisson_short_syntax.py file in your favorite text editor. Note that the
file is a regular Python source code.

from sfepy import data_dir

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

The filename_mesh variable points to the file containing the mesh for the particular problem. SfePy supports a variety
of mesh formats.

materials = {
'coef': ({'val' : 1.0},),
}

Here we define just a constant coefficient 𝑐 of the Poisson equation, using the β€˜values’ attribute. Other possible attribute
is β€˜function’ for material coefficients computed/obtained at runtime.
Many finite element problems require the definition of material parameters. These can be handled in SfePy with material
variables which associate the material parameters with the corresponding region of the mesh.

regions = {
'Omega' : 'all', # or 'cells of group 6'
'Gamma_Left' : ('vertices in (x < 0.00001)', 'facet'),
'Gamma_Right' : ('vertices in (x > 0.099999)', 'facet'),
}

Regions assign names to various parts of the finite element mesh. The region names can later be referred to, for
example when specifying portions of the mesh to apply boundary conditions to. Regions can be specified in a variety
of ways, including by element or by node. Here, β€˜Omega’ is the elemental domain over which the PDE is solved and
β€˜Gamma_Left’ and β€˜Gamma_Right’ define surfaces upon which the boundary conditions will be applied.

1.3. Tutorial 13
SfePy Documentation, Release version: 2022.1+git.f9873484

fields = {
'temperature': ('real', 1, 'Omega', 1)
}

A field is used mainly to define the approximation on a (sub)domain, i.e. to define the discrete spaces π‘‰β„Ž , where we
seek the solution.
The Poisson equation can be used to compute e.g. a temperature distribution, so let us call our field β€˜temperature’. On
the region β€˜Omega’ it will be approximated using linear finite elements.
A field in a given region defines the finite element approximation. Several variables can use the same field, see below.
variables = {
't': ('unknown field', 'temperature', 0),
's': ('test field', 'temperature', 't'),
}

One field can be used to generate discrete degrees of freedom (DOFs) of several variables. Here the unknown variable
(the temperature) is called β€˜t’, it’s associated DOF name is β€˜t.0’ – this will be referred to in the Dirichlet boundary
section (ebc). The corresponding test variable of the weak formulation is called β€˜s’. Notice that the β€˜dual’ item of a
test variable must specify the unknown it corresponds to.
For each unknown (or state) variable there has to be a test (or virtual) variable defined, as usual in weak formulation of
PDEs.
ebcs = {
't1': ('Gamma_Left', {'t.0' : 2.0}),
't2', ('Gamma_Right', {'t.0' : -2.0}),
}

Essential (Dirichlet) boundary conditions can be specified as above.


Boundary conditions place restrictions on the finite element formulation and create a unique solution to the problem.
Here, we specify that a temperature of +2 is applied to the left surface of the mesh and a temperature of -2 is applied
to the right surface.
integrals = {
'i': 2,
}

Integrals specify which numerical scheme to use. Here we are using a 2nd order quadrature over a 3 dimensional space.
equations = {
'Temperature' : """dw_laplace.i.Omega( coef.val, s, t ) = 0"""
}

The equation above directly corresponds to the discrete version of (1.2), namely: Find 𝑑 ∈ π‘‰β„Ž , such that
∫︁
𝑇
𝑠 ( 𝑐 𝐺𝑇 𝐺)𝑑 = 0, βˆ€π‘  ∈ π‘‰β„Ž0 ,
Ξ©β„Ž

where βˆ‡π‘’ β‰ˆ 𝐺𝑒.
The equations block is the heart of the SfePy problem description file. Here, we are specifying that the Laplacian of the
temperature (in the weak formulation) is 0, where coef.val is a material constant. We are using the β€˜i’ integral defined
previously, over the domain specified by the region β€˜Omega’.
The above syntax is useful for defining custom integrals with user-defined quadrature points and weights, see Integrals.
The above uniform integration can be more easily achieved by:

14 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

equations = {
'Temperature' : """dw_laplace.2.Omega( coef.val, s, t ) = 0"""
}

The integration order is specified directly in place of the integral name. The integral definition is superfluous in this
case.

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton',
{'i_max' : 1,
'eps_a' : 1e-10,
}),
}

Here, we specify the linear and nonlinear solver kinds and options. See sfepy.solvers.ls, sfepy.solvers.nls
and sfepy.solvers.ts_solvers for available solvers and their parameters.. Even linear problems are solved by a
nonlinear solver (KISS rule) – only one iteration is needed and the final residual is obtained for free. Note that we do
not need to define a time-stepping solver here - the problem is stationary and the default 'ts.stationary' solver is
created automatically.

options = {
'nls' : 'newton',
'ls' : 'ls',
}

The solvers to use are specified in the options block. We can define multiple solvers with different convergence param-
eters.
That’s it! Now it is possible to proceed as described in Invoking SfePy from the Command Line.

1.3.5 Interactive Example: Linear Elasticity

This example shows how to use SfePy interactively, but also how to make a custom simulation script. We will use
IPython interactive shell which allows more flexible and intuitive work (but you can use standard Python shell as well).
We wish to solve the following linear elasticity problem:
πœ•πœŽπ‘–π‘— (𝑒)
βˆ’ + 𝑓𝑖 = 0 in Ω, 𝑒 = 0 on Ξ“1 , Β―1 on Ξ“2 ,
𝑒1 = 𝑒 (1.3)
πœ•π‘₯𝑗
πœ•π‘’
where the stress is defined as πœŽπ‘–π‘— = 2πœ‡π‘’π‘–π‘— +πœ†π‘’π‘˜π‘˜ 𝛿𝑖𝑗 , πœ†, πœ‡ are the Lamé’s constants, the strain is 𝑒𝑖𝑗 (𝑒) = 21 ( πœ•π‘₯
πœ•π‘’π‘–
𝑗
+ πœ•π‘₯𝑗𝑖 )
and 𝑓 are volume forces. This can be written in general form as πœŽπ‘–π‘— (𝑒) = π·π‘–π‘—π‘˜π‘™ π‘’π‘˜π‘™ (𝑒), where in our case π·π‘–π‘—π‘˜π‘™ =
πœ‡(π›Ώπ‘–π‘˜ 𝛿𝑗𝑙 + 𝛿𝑖𝑙 π›Ώπ‘—π‘˜ ) + πœ† 𝛿𝑖𝑗 π›Ώπ‘˜π‘™ .
In the weak form the equation (1.3) is
∫︁ ∫︁
π·π‘–π‘—π‘˜π‘™ π‘’π‘˜π‘™ (𝑒)𝑒𝑖𝑗 (𝑣) + 𝑓 𝑖 𝑣𝑖 = 0 , (1.4)
Ξ© Ξ©

where 𝑣 is the test function, and both 𝑒, 𝑣 belong to a suitable function space.
Hint: Whenever you create a new object (e.g. a Mesh instance, see below), try to print it using the print statement – it
will give you insight about the object internals.
The whole example summarized in a script is available below in Complete Example as a Script.
In the SfePy top-level directory run

1.3. Tutorial 15
SfePy Documentation, Release version: 2022.1+git.f9873484

ipython

In [1]: import numpy as nm


In [2]: from sfepy.discrete.fem import Mesh, FEDomain, Field

Read a finite element mesh, that defines the domain Ω.

In [3]: mesh = Mesh.from_file('meshes/2d/rectangle_tri.mesh')

Create a domain. The domain allows defining regions or subdomains.

In [4]: domain = FEDomain('domain', mesh)

Define the regions – the whole domain Ω, where the solution is sought, and Ξ“1 , Ξ“2 , where the boundary conditions will
be applied. As the domain is rectangular, we first get a bounding box to get correct bounds for selecting the boundary
edges.

In [5]: min_x, max_x = domain.get_mesh_bounding_box()[:, 0]


In [6]: eps = 1e-8 * (max_x - min_x)
In [7]: omega = domain.create_region('Omega', 'all')
In [8]: gamma1 = domain.create_region('Gamma1',
...: 'vertices in x < %.10f ' % (min_x + eps),
...: 'facet')
In [9]: gamma2 = domain.create_region('Gamma2',
...: 'vertices in x > %.10f ' % (max_x - eps),
...: 'facet')

Next we define the actual finite element approximation using the Field class.

In [10]: field = Field.from_args('fu', nm.float64, 'vector', omega,


....: approx_order=2)

Using the field fu, we can define both the unknown variable 𝑒 and the test variable 𝑣.

In [11]: from sfepy.discrete import (FieldVariable, Material, Integral, Function,


....: Equation, Equations, Problem)

In [12]: u = FieldVariable('u', 'unknown', field)


In [13]: v = FieldVariable('v', 'test', field, primary_var_name='u')

Before we can define the terms to build the equation of linear elasticity, we have to create also the materials, i.e. define
the (constitutive) parameters. The linear elastic material m will be defined using the two LamΓ© constants πœ† = 1, πœ‡ = 1.
The volume forces will be defined also as a material as a constant (column) vector [0.02, 0.01]𝑇 .

In [14]: from sfepy.mechanics.matcoefs import stiffness_from_lame

In [15]: m = Material('m', D=stiffness_from_lame(dim=2, lam=1.0, mu=1.0))


In [16]: f = Material('f', val=[[0.02], [0.01]])

One more thing needs to be defined – the numerical quadrature that will be used to integrate each term over its domain.

In [17]: integral = Integral('i', order=3)

Now we are ready to define the two terms and build the equations.

16 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

In [18]: from sfepy.terms import Term

In [19]: t1 = Term.new('dw_lin_elastic(m.D, v, u)',


....: integral, omega, m=m, v=v, u=u)

In [20]: t2 = Term.new('dw_volume_lvf(f.val, v)',


....: integral, omega, f=f, v=v)
In [21]: eq = Equation('balance', t1 + t2)
In [22]: eqs = Equations([eq])

The equations have to be completed by boundary conditions. Let us clamp the left edge Ξ“1 , and shift the right edge Ξ“2
in the π‘₯ direction a bit, depending on the 𝑦 coordinate.

In [23]: from sfepy.discrete.conditions import Conditions, EssentialBC

In [24]: fix_u = EssentialBC('fix_u', gamma1, {'u.all' : 0.0})


In [25]: def shift_u_fun(ts, coors, bc=None, problem=None, shift=0.0):
....: val = shift * coors[:,1]**2
....: return val
In [26]: bc_fun = Function('shift_u_fun', shift_u_fun,
....: extra_args={'shift' : 0.01})
In [27]: shift_u = EssentialBC('shift_u', gamma2, {'u.0' : bc_fun})

The last thing to define before building the problem are the solvers. Here we just use a sparse direct SciPy solver and
the SfePy Newton solver with default parameters. We also wish to store the convergence statistics of the Newton solver.
As the problem is linear it should converge in one iteration.

In [28]: from sfepy.base.base import IndexedStruct


In [29]: from sfepy.solvers.ls import ScipyDirect
In [30]: from sfepy.solvers.nls import Newton

In [31]: ls = ScipyDirect({})
In [32]: nls_status = IndexedStruct()
In [33]: nls = Newton({}, lin_solver=ls, status=nls_status)

Now we are ready to create a Problem instance.

In [34]: pb = Problem('elasticity', equations=eqs)

The Problem has several handy methods for debugging. Let us try saving the regions into a VTK file.

In [35]: pb.save_regions_as_groups('regions')

And view them.

In [36]: from sfepy.postprocess.viewer import Viewer

In [37]: view = Viewer('regions.vtk')


In [38]: view()

You should see this:

1.3. Tutorial 17
SfePy Documentation, Release version: 2022.1+git.f9873484

Finally, we set the boundary conditions and the top-level solver , solve the problem, save and view the results. For
stationary problems, the top-level solver needs not to be a time-stepping solver - when a nonlinear solver is set instead,
the default 'ts.stationary' time-stepping solver is created automatically.

In [39]: pb.set_bcs(ebcs=Conditions([fix_u, shift_u]))


In [40]: pb.set_solver(nls)

In [41]: status = IndexedStruct()


In [42]: variables = pb.solve(status=status)

In [43]: print('Nonlinear solver status:\n', nls_status)


In [44]: print('Stationary solver status:\n', status)

In [45]: pb.save_state('linear_elasticity.vtk', variables)


In [46]: view = Viewer('linear_elasticity.vtk')
In [47]: view()

This is the resulting image:

18 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

The default view is not very fancy. Let us show the displacements by shifting the mesh. Close the previous window
and do:

In [48]: view(vector_mode='warp_norm', rel_scaling=2,


...: is_scalar_bar=True, is_wireframe=True)

And the result is:

See the docstring of view() and play with its options.

1.3. Tutorial 19
SfePy Documentation, Release version: 2022.1+git.f9873484

Complete Example as a Script

The source code: linear_elastic_interactive.py.


This file should be run from the top-level SfePy source directory so it can find the mesh file correctly. Please note that
the provided example script may differ from above tutorial in some minor details.
1 #!/usr/bin/env python
2 from argparse import ArgumentParser
3 import numpy as nm
4

5 import sys
6 sys.path.append('.')
7

8 from sfepy.base.base import IndexedStruct


9 from sfepy.discrete import (FieldVariable, Material, Integral, Function,
10 Equation, Equations, Problem)
11 from sfepy.discrete.fem import Mesh, FEDomain, Field
12 from sfepy.terms import Term
13 from sfepy.discrete.conditions import Conditions, EssentialBC
14 from sfepy.solvers.ls import ScipyDirect
15 from sfepy.solvers.nls import Newton
16 from sfepy.postprocess.viewer import Viewer
17 from sfepy.mechanics.matcoefs import stiffness_from_lame
18

19

20 def shift_u_fun(ts, coors, bc=None, problem=None, shift=0.0):


21 """
22 Define a displacement depending on the y coordinate.
23 """
24 val = shift * coors[:,1]**2
25

26 return val
27

28 helps = {
29 'show' : 'show the results figure',
30 }
31

32 def main():
33 from sfepy import data_dir
34

35 parser = ArgumentParser()
36 parser.add_argument('--version', action='version', version='%(prog)s')
37 parser.add_argument('-s', '--show',
38 action="store_true", dest='show',
39 default=False, help=helps['show'])
40 options = parser.parse_args()
41

42 mesh = Mesh.from_file(data_dir + '/meshes/2d/rectangle_tri.mesh')


43 domain = FEDomain('domain', mesh)
44

45 min_x, max_x = domain.get_mesh_bounding_box()[:,0]


46 eps = 1e-8 * (max_x - min_x)
47 omega = domain.create_region('Omega', 'all')
(continues on next page)

20 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


48 gamma1 = domain.create_region('Gamma1',
49 'vertices in x < %.10f ' % (min_x + eps),
50 'facet')
51 gamma2 = domain.create_region('Gamma2',
52 'vertices in x > %.10f ' % (max_x - eps),
53 'facet')
54

55 field = Field.from_args('fu', nm.float64, 'vector', omega,


56 approx_order=2)
57

58 u = FieldVariable('u', 'unknown', field)


59 v = FieldVariable('v', 'test', field, primary_var_name='u')
60

61 m = Material('m', D=stiffness_from_lame(dim=2, lam=1.0, mu=1.0))


62 f = Material('f', val=[[0.02], [0.01]])
63

64 integral = Integral('i', order=3)


65

66 t1 = Term.new('dw_lin_elastic(m.D, v, u)',
67 integral, omega, m=m, v=v, u=u)
68 t2 = Term.new('dw_volume_lvf(f.val, v)', integral, omega, f=f, v=v)
69 eq = Equation('balance', t1 + t2)
70 eqs = Equations([eq])
71

72 fix_u = EssentialBC('fix_u', gamma1, {'u.all' : 0.0})


73

74 bc_fun = Function('shift_u_fun', shift_u_fun,


75 extra_args={'shift' : 0.01})
76 shift_u = EssentialBC('shift_u', gamma2, {'u.0' : bc_fun})
77

78 ls = ScipyDirect({})
79

80 nls_status = IndexedStruct()
81 nls = Newton({}, lin_solver=ls, status=nls_status)
82

83 pb = Problem('elasticity', equations=eqs)
84 pb.save_regions_as_groups('regions')
85

86 pb.set_bcs(ebcs=Conditions([fix_u, shift_u]))
87

88 pb.set_solver(nls)
89

90 status = IndexedStruct()
91 variables = pb.solve(status=status)
92

93 print('Nonlinear solver status:\n', nls_status)


94 print('Stationary solver status:\n', status)
95

96 pb.save_state('linear_elasticity.vtk', variables)
97

98 if options.show:
99 view = Viewer('linear_elasticity.vtk')
(continues on next page)

1.3. Tutorial 21
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


100 view(vector_mode='warp_norm', rel_scaling=2,
101 is_scalar_bar=True, is_wireframe=True)
102

103 if __name__ == '__main__':


104 main()

1.4 User’s Guide

This manual provides reference documentation to SfePy from a user’s perspective.

1.4.1 Running a Simulation

The following should be run in the top-level directory of the SfePy source tree after compiling the C extension files.
See Installation for full installation instructions info. The $ indicates the command prompt of your terminal.

Basic Usage

β€’ $ ./simple.py sfepy/examples/diffusion/poisson_short_syntax.py

– Creates cylinder.vtk

β€’ $ ./simple.py sfepy/examples/navier_stokes/stokes.py

– Creates channels_symm944t.vtk

Applications

β€’ Phononic Materials

– $ ./phonon.py -p sfepy/examples/phononic/band_gaps.py

βˆ— see sfepy/examples/phononic/output/

Using Command Wrapper

All top-level SfePy scripts (applications) can be run via single sfepy-run wrapper:

$ ./sfepy-run
usage: sfepy-run [command] [options]

Simple wrapper for main SfePy commands.

positional arguments:
{extractor,phonon,postproc,probe,simple}
Available SfePy command(s).
options Additional options passed directly to selected
[command].
(continues on next page)

22 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
-w, --window use alternative (pythonw) interpreter

Notes

β€’ This is a β€œnew” supported method. Any SfePy script can be still run as stand-alone (as mentioned above).
β€’ Both β€œinplace” and β€œsystem-wide” installations are supported.

Running Tests

The tests are based on pytest and can be run using:

python -c "import sfepy; sfepy.test()"

See Testing Installation for additional information.

Computations and Examples

The example problems in the examples directory can be computed by the script simple.py which is in the top-level
directory of the SfePy distribution. If it is run without arguments, a help message is printed:

$ ./simple.py
Usage: simple.py [options] filename_in

Solve partial differential equations given in a SfePy problem definition file.

Example problem definition files can be found in ``sfepy/examples/`` directory


of the SfePy top-level directory.

Both normal and parametric study runs are supported. A parametric study
allows repeated runs for varying some of the simulation parameters - see
``sfepy/examples/diffusion/poisson_parametric_study.py`` file.

Options:
--version show program's version number and exit
-h, --help show this help message and exit
-c "key : value, ...", --conf="key : value, ..."
override problem description file items, written as
python dictionary without surrounding braces
-O "key : value, ...", --options="key : value, ..."
override options item of problem description, written
as python dictionary without surrounding braces
-d "key : value, ...", --define="key : value, ..."
pass given arguments written as python dictionary
without surrounding braces to define() function of
problem description file
(continues on next page)

1.4. User’s Guide 23


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


-o filename basename of output file(s) [default: <basename of
input file>]
--format=format output file format, one of: {vtk, h5} [default: vtk]
--save-restart=mode if given, save restart files according to the given
mode.
--load-restart=filename
if given, load the given restart file
--log=file log all messages to specified file (existing file will
be overwritten!)
-q, --quiet do not print any messages to screen
--save-ebc save a zero solution with applied EBCs (Dirichlet
boundary conditions)
--save-ebc-nodes save a zero solution with added non-zeros in EBC
(Dirichlet boundary conditions) nodes - scalar
variables are shown using colors, vector variables
using arrows with non-zero components corresponding to
constrained components
--save-regions save problem regions as meshes
--save-regions-as-groups
save problem regions in a single mesh but mark them by
using different element/node group numbers
--save-field-meshes save meshes of problem fields (with extra DOF nodes)
--solve-not do not solve (use in connection with --save-*)
--list=what list data, what can be one of: {terms, solvers}

Additional (stand-alone) examples are in the sfepy/examples/ directory, e.g.:

$ python sfepy/examples/large_deformation/compare_elastic_materials.py

Parametric study example:

$ ./simple.py sfepy/examples/diffusion/poisson_parametric_study.py

Common Tasks

β€’ Run a simulation:

./simple.py sfepy/examples/diffusion/poisson_short_syntax.py
./simple.py sfepy/examples/diffusion/poisson_short_syntax.py -o some_results # ->␣
Λ“β†’produces some_results.vtk

β€’ Print available terms:

./simple.py --list=terms

β€’ Run a simulation and also save Dirichlet boundary conditions:

./simple.py --save-ebc sfepy/examples/diffusion/poisson_short_syntax.py # ->␣


Λ“β†’produces an additional .vtk file with BC visualization

β€’ Use a restart file to continue an interrupted simulation:


– Warning: This feature is preliminary and does not support terms with internal state.

24 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

– Run:

./simple.py sfepy/examples/large_deformation/balloon.py --save-restart=-1

and break the computation after a while (hit Ctrl-C). The mode --save-restart=-1 is currently the only
supported mode. It saves a restart file for each time step, and only the last computed time step restart file is
kept.
– A file named 'unit_ball.restart-??.h5' should be created, where '??' indicates the last stored time
step. Let us assume it is 'unit_ball.restart-04.h5', i.e. the fifth step.
– Restart the simulation by:

./simple.py sfepy/examples/large_deformation/balloon.py --load-restart=unit_


Λ“β†’ball.restart-04.h5

The simulation should continue from the next time step. Verify that by running:

./simple.py sfepy/examples/large_deformation/balloon.py

and compare the residuals printed in the corresponding time steps.

1.4.2 Visualization of Results

resview.py – PyVista

Quick visualisation of the SfePy results can be done by resview.py script, which uses PyVista visualisation toolkit
(need to be installed).
The help message of the script is:

usage: resview.py [-h] [-f field_spec [field_spec ...]]


[--fields-map map [map ...]] [-s step] [-l] [-e] [-w field]
[--factor factor] [--opacity opacity] [--color-map cmap]
[--axes-options options [options ...]] [--no-axes]
[--position-vector position_vector] [--no-labels]
[--label-position position] [--no-scalar-bars]
[--scalar-bar-size size] [--scalar-bar-position position]
[-v position] [-a output_file] [-r rate] [-o output_file]
[--off-screen] [-2]
filenames [filenames ...]

This is a script for quick VTK-based visualizations of finite element


computations results.

Examples
--------
The examples assume that
``python -c "import sfepy; sfepy.test('--output-dir=output-tests')"``
has been run successfully and the resulting data files are present.

- View data in output-tests/test_navier_stokes.vtk.

$ python resview.py output-tests/navier_stokes-navier_stokes.vtk


(continues on next page)

1.4. User’s Guide 25


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

- Customize the above output:


plot0: field "p", switch on edges,
plot1: field "u", surface with opacity 0.4, glyphs scaled by factor 2e-2.

$ python resview.py output-tests/navier_stokes-navier_stokes.vtk -f p:e:p0 u:o.4:p1␣


Λ“β†’u:g:f2e-2:p1

- As above, but glyphs are scaled by the factor determined automatically as


20% of the minimum bounding box size.

$ python resview.py output-tests/navier_stokes-navier_stokes.vtk -f p:e:p0 u:o.4:p1␣


Λ“β†’u:g:f10%:p1

- View data and take a screenshot.

$ python resview.py output-tests/diffusion-poisson.vtk -o image.png

- Take a screenshot without a window popping up.

$ python resview.py output-tests/diffusion-poisson.vtk -o image.png --off-screen

- Create animation from output-tests/diffusion-time_poisson.*.vtk.

$ python resview.py output-tests/diffusion-time_poisson.*.vtk -a mov.mp4

- Create animation from output-tests/test_hyperelastic.*.vtk,


set frame rate to 3, plot displacements and mooney_rivlin_stress.

$ python resview.py output-tests/test_hyperelastic_TL.*.vtk -f u:wu:e:p0 mooney_rivlin_


Λ“β†’stress:p1 -a mov.mp4 -r 3

positional arguments:
filenames

optional arguments:
-h, --help show this help message and exit
-f field_spec [field_spec ...], --fields field_spec [field_spec ...]
fields to plot, options separated by ":" are possible:
"cX" - plot only Xth field component; "e" - print
edges; "fX" - scale factor for warp/glyphs, see
--factor option; "g - glyphs (for vector fields only),
scale by factor; "tX" - plot X streamlines, gradient
employed for scalar fields; "mX" - plot cells with
mat_id=X; "oX" - set opacity to X; "pX" - plot in slot
X; "r" - recalculate cell data to point data; "sX" -
plot data in step X; "vX" - plotting style: s=surface,
w=wireframe, p=points; "wX" - warp mesh by vector
field X, scale by factor
--fields-map map [map ...]
map fields and cell groups, e.g. 1:u1,p1 2:u2,p2
-s step, --step step select data in a given time step
(continues on next page)

26 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


-l, --outline plot mesh outline
-e, --edges plot cell edges
-w field, --warp field
warp mesh by vector field
--factor factor scaling factor for mesh warp and glyphs. Append "%" to
scale relatively to the minimum bounding box size.
--opacity opacity set opacity [default: 1.0]
--color-map cmap set color_map, e.g. hot, cool, bone, etc. [default:
viridis]
--axes-options options [options ...]
options for directional axes, e.g. xlabel="z1"
ylabel="z2", zlabel="z3"
--no-axes hide orientation axes
--position-vector position_vector
define positions of plots [default: "0, 0, 1.6"]
--no-labels hide plot labels
--label-position position
define position of plot labels [default: "-1, -1, 0,
0.2"]
--no-scalar-bars hide scalar bars
--scalar-bar-size size
define size of scalar bars [default: "0.15, 0.05"]
--scalar-bar-position position
define position of scalar bars [default: "0.8, 0.02,
0, 1.5"]
-v position, --view position
camera azimuth, elevation angles, and optionally zoom
factor [default: "225,75,0.9"]
-a output_file, --animation output_file
create animation, mp4 file type supported
-r rate, --frame-rate rate
set framerate for animation
-o output_file, --screenshot output_file
save screenshot to file
--off-screen off screen plots, e.g. when screenshotting
-2, --2d-view 2d view of XY plane

The first example in the above help:

./resview.py output-tests/test_navier_stokes.vtk

produces:

1.4. User’s Guide 27


SfePy Documentation, Release version: 2022.1+git.f9873484

Using -f p:e:p0 u:o.4:p1 u:g:f2e-2:p1 arguments:

./resview.py output-tests/test_navier_stokes.vtk -f p:e:p0 u:o.4:p1 u:g:f2e-2:p1

the output is split into plots plot:0 and plot:1, where these plots contain:
β€’ plot:0: field p, mesh edges are switched on
β€’ plot:1: magnitude of vector field u displayed as the surface with opacity set to 0.4; glyphs related to field u and
scaled by factor 2e-2

28 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

The argument -o filename.png takes the screenshot of the produced view:

./resview.py output-tests/test_poisson.vtk -o image.png

1.4. User’s Guide 29


SfePy Documentation, Release version: 2022.1+git.f9873484

postproc.py – Mayavi2

The postproc.py script can be used for quick postprocessing and visualization of the SfePy results. It requires mayavi2
installed on your system.
The help message of the script is:
usage: postproc.py [-h] [--version] [--debug] [-o filename]
[--output-dir directory] [-n] [--no-offscreen]
[-a <ffmpeg-supported format>]
[--ffmpeg-options <ffmpeg options>] [--step step]
[--time time] [-w] [--all] [--only-names list of names]
[-l] [--ranges name1,min1,max1:name2,min2,max2:...]
[-r resolution] [--layout layout] [--3d]
[--view angle,angle[,distance[,focal_point]]]
[--roll angle] [--parallel-projection] [--fgcolor R,G,B]
[--bgcolor R,G,B] [--colormap colormap]
[--anti-aliasing value] [-b] [--wireframe]
[--group-names name1,...,nameN:...]
[--subdomains mat_id_name,threshold_limits,single_color]
[-d "var_name0,function_name0,par0=val0,par1=val1,...:var_name1,..."]
[--scalar-mode mode] [--vector-mode mode] [-s scale]
[--clamping] [--opacity opacity] [--rel-text-width width]
filenames [filenames ...]

This is a script for quick Mayavi-based visualizations of finite element


computations results.

Examples
--------
The examples assume that
``python -c "import sfepy; sfepy.test('--output-dir=output-tests')"``
has been run successfully and the resulting data files are present.

- view data in output-tests/navier_stokes-navier_stokes.vtk

$ python postproc.py output-tests/navier_stokes-navier_stokes.vtk


$ python postproc.py output-tests/navier_stokes-navier_stokes.vtk --3d

- save a snapshot image and exit

$ python postproc.py output-tests/diffusion-poisson.vtk -o image.png -n

- save a snapshot image without off-screen rendering and exit

$ python postproc.py output-tests/diffusion-poisson.vtk -o image.png -n --no-offscreen

- create animation (forces offscreen rendering) from


output-tests/diffusion-time_poisson.*.vtk

$ python postproc.py output-tests/diffusion-time_poisson.*.vtk -a mov

- create animation (forces offscreen rendering) from


output-tests/test_hyperelastic_TL.*.vtk
(continues on next page)

30 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

The range specification for the displacements 'u' is required, as


output-tests/test_hyperelastic_TL.00.vtk contains only zero
displacements which leads to invisible glyph size.

$ python postproc.py output-tests/test_hyperelastic_TL.*.vtk --ranges=u,0,0.02 -a mov

- same as above, but slower frame rate

$ python postproc.py output-tests/test_hyperelastic_TL.*.vtk --ranges=u,0,0.02 -a mov -


Λ“β†’-ffmpeg-options="-framerate 2"

positional arguments:
filenames

optional arguments:
-h, --help show this help message and exit
--version show program's version number and exit
--debug automatically start debugger when an exception is
raised

Output Options:
-o filename, --output filename
view image file name [default: "view.png"]
--output-dir directory
output directory for saving view images; ignored when
-o option is given, as the directory part of the
filename is taken instead [default: "."]
-n, --no-show do not call mlab.show()
--no-offscreen force no offscreen rendering for --no-show
-a <ffmpeg-supported format>, --animation <ffmpeg-supported format>
if set to a ffmpeg-supported format (e.g. mov, avi,
mpg), ffmpeg is installed and results of multiple time
steps are given, an animation is created in the same
directory as the view images
--ffmpeg-options <ffmpeg options>
ffmpeg animation encoding options (enclose in
"")[default: "-framerate 10"]

Data Options:
--step step set the time step. Negative indices are allowed, -1
means the last step. The closest higher step is used
if the desired one is not available. Has precedence
over --time. [default: the first step]
--time time set the time. The closest higher time is used if the
desired one is not available. [default: None]
-w, --watch watch the results file for changes (single file mode
only)
--all draw all data (normally, node_groups and mat_id are
omitted)
--only-names list of names
draw only named data
(continues on next page)

1.4. User’s Guide 31


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


-l, --list-ranges do not plot, only list names and ranges of all data
--ranges name1,min1,max1:name2,min2,max2:...
force data ranges [default: automatic from data]

View Options:
-r resolution, --resolution resolution
image resolution in NxN format [default: shorter axis:
600; depends on layout: for rowcol it is 800x600]
--layout layout layout for multi-field plots, one of: rowcol, colrow,
row, col, row#n,col#n, where #n is the number of plots
in the specified direction [default: rowcol]
--3d 3d plot mode
--view angle,angle[,distance[,focal_point]]
camera azimuth, elevation angles, and optionally also
distance and focal point coordinates (without []) as
in `mlab.view()` [default: if --3d is True: "45,45",
else: "0,0"]
--roll angle camera roll angle [default: 0.0]
--parallel-projection
use parallel projection
--fgcolor R,G,B foreground color, that is the color of all text
annotation labels (axes, orientation axes, scalar bar
labels) [default: 0.0,0.0,0.0]
--bgcolor R,G,B background color [default: 1.0,1.0,1.0]
--colormap colormap mayavi2 colormap name [default: blue-red]
--anti-aliasing value
value of anti-aliasing [default: mayavi2 default]

Custom Plots Options:


-b, --scalar-bar show scalar bar for each data
--wireframe show wireframe of mesh surface for each data
--group-names name1,...,nameN:...
superimpose plots of data in each group
--subdomains mat_id_name,threshold_limits,single_color
superimpose surfaces of subdomains over each data;
example value: mat_id,0,None,True
-d "var_name0,function_name0,par0=val0,par1=val1,...:var_name1,...", --domain-specific
Λ“β†’"var_name0,function_name0,par0=val0,par1=val1,...:var_name1,..."

domain specific drawing functions and configurations

Mayavi Options:
--scalar-mode mode mode for plotting scalars with --3d, one of:
cut_plane, iso_surface, both [default: iso_surface]
--vector-mode mode mode for plotting vectors, one of: arrows, norm,
arrows_norm, warp_norm [default: arrows_norm]
-s scale, --scale-glyphs scale
relative scaling of glyphs (vector field
visualization) [default: 0.05]
--clamping glyph clamping mode
--opacity opacity opacity in [0.0, 1.0]. Can be given either globally as
a single float, or per module, e.g.
"wireframe=0.1,scalar_cut_plane=0.5". Possible
(continues on next page)

32 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


keywords are: wireframe, scalar_cut_plane,
vector_cut_plane, surface, iso_surface,
arrows_surface, glyphs. [default: 1.0]
--rel-text-width width
relative text annotation width [default: 0.02]

As a simple example, try:


$ ./simple.py sfepy/examples/diffusion/poisson_short_syntax.py
$ ./postproc.py cylinder.vtk

The following window should display:

The -l switch lists information contained in a results file, e.g.:


$ ./postproc.py -l cylinder.vtk
sfepy: 0: cylinder.vtk
point scalars
(continues on next page)

1.4. User’s Guide 33


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


"node_groups" (354,) range: 0 0 l2_norm_range: 0.0 0.0
"t" (354,) range: -2.0 2.0 l2_norm_range: 0.0106091 2.0
cell scalars
"mat_id" (1348,) range: 6 6 l2_norm_range: 6.0 6.0

1.4.3 Problem Description File

Here we discuss the basic items that users have to specify in their input files. For complete examples, see the problem
description files in the sfepy/examples/ directory of SfePy.

Long Syntax

Besides the short syntax described below there is (due to history) also a long syntax which is explained in prob-
lem_desc_file_long. The short and long syntax can be mixed together in one description file.

FE Mesh

A FE mesh defining a domain geometry can be stored in several formats:


β€’ legacy VTK (.vtk)
β€’ custom HDF5 file (.h5)
β€’ medit mesh file (.mesh)
β€’ tetgen mesh files (.node, .ele)
β€’ comsol text mesh file (.txt)
β€’ abaqus text mesh file (.inp)
β€’ avs-ucd text mesh file (.inp)
β€’ hypermesh text mesh file (.hmascii)
β€’ hermes3d mesh file (.mesh3d)
β€’ nastran text mesh file (.bdf)
β€’ gambit neutral text mesh file (.neu)
β€’ salome/pythonocc med binary mesh file (.med)
Example:

filename_mesh = 'meshes/3d/cylinder.vtk'

The VTK and HDF5 formats can be used for storing the results. The format can be selected in options, see Miscella-
neous.
The following geometry elements are supported:

34 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

Note the orientation of the vertices matters, the figure displays the correct orientation when interpreted in a right-handed
coordinate system.

Regions

Regions serve to select a certain part of the computational domain using topological entities of the FE mesh. They are
used to define the boundary conditions, the domains of terms and materials etc.
Let us denote D the maximal dimension of topological entities. For volume meshes it is also the dimension of space
the domain is embedded in. Then the following topological entities can be defined on the mesh (notation follows
[Logg2012]):

topological entity dimension co-dimension


vertex 0 D
edge 1 D-1
face 2 D-2
facet D-1 1
cell D 0

If D = 2, faces are not defined and facets are edges. If D = 3, facets are faces.
Following the above definitions, a region can be of different kind:
β€’ cell, facet, face, edge, vertex - entities of higher dimension are not included.
β€’ cell_only, facet_only, face_only, edge_only, vertex_only - only the specified entities are included,
other entities are empty sets, so that set-like operators still work, see below.

1.4. User’s Guide 35


SfePy Documentation, Release version: 2022.1+git.f9873484

β€’ The cell kind is the most general and should be used with volume terms. It is also the default if the kind is not
specified in region definition.
β€’ The facet kind (same as edge in 2D and face in 3D) is to be used with boundary (surface integral) terms.
β€’ The vertex (same as vertex_only) kind can be used with point-wise defined terms (e.g. point loads).
The kinds allow a clear distinction between regions of different purpose (volume integration domains, surface domains,
etc.) and could be uses to lower memory usage.
A region definition involves topological entity selections combined with set-like operators. The set-like operators can
result in intermediate regions that have the cell kind. The desired kind is set to the final region, removing unneeded
entities. Most entity selectors are defined in terms of vertices and cells - the other entities are computed as needed.

Topological Entity Selection

topological entity selection explanation


all all entities of the mesh
vertices of surface surface of the mesh
vertices of group <integer> vertices of given group
vertices of set <str> vertices of a given named vertex set2
vertices in <expr> vertices given by an expression3
vertices by <function> vertices given by a function of coordinates4
vertex <id>[, <id>, ...] vertices given by their ids
vertex in r.<name of another region> any single vertex in the given region
cells of group <integer> cells of given group
cells by <efunction> cells given by a function of coordinates5
cell <id>[, <id>, ...], cells given by their ids
copy r.<name of another region> a copy of the given region
r.<name of another region> a reference to the given region

2 Only if mesh format supports reading boundary condition vertices as vertex sets.
3 <expr> is a logical expression like (y <= 0.1) & (x < 0.2). In 2D use x, y, in 3D use x, y and z. & stands for logical and, | stands for
logical or.
4 <function> is a function with signature fun(coors, domain=None), where coors are coordinates of mesh vertices.
5 <efunction> is a function with signature fun(coors, domain=None), where coors are coordinates of mesh cell centroids.

36 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

topological entity selection footnotes

set-like operator explanation


+v vertex union
+e edge union
+f face union
+s facet union
+c cell union
-v vertex difference
-e edge difference
-f face difference
-s facet difference
-c cell difference
*v vertex intersection
*e edge intersection
*f face intersection
*s facet intersection
*c cell intersection

Region Definition Syntax

Regions are defined by the following Python dictionary:

regions = {
<name> : (<selection>, [<kind>], [<parent>], [{<misc. options>}]),
}

or:

regions = {
<name> : <selection>,
}

Example definitions:

regions = {
'Omega' : 'all',
'Right' : ('vertices in (x > 0.99)', 'facet'),
'Gamma1' : ("""(cells of group 1 *v cells of group 2)
+v r.Right""", 'facet', 'Omega'),
'Omega_B' : 'vertices by get_ball',
}

The Omega_B region illustrates the selection by a function (see Topological Entity Selection). In this example, the
function is:

import numpy as nm

def get_ball(coors, domain=None):


x, y, z = coors[:, 0], coors[:, 1], coors[:, 2]

(continues on next page)

1.4. User’s Guide 37


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


r = nm.sqrt(x**2 + y**2 + z**2)
flag = nm.where((r < 0.1))[0]

return flag

The function needs to be registered in Functions:

functions = {
'get_ball' : (get_ball,),
}

The mirror region can be defined explicitly as:

regions = {
'Top': ('r.Y *v r.Surf1', 'facet', 'Y', {'mirror_region': 'Bottom'}),
'Bottom': ('r.Y *v r.Surf2', 'facet', 'Y', {'mirror_region': 'Top'}),
}

Fields

Fields correspond to FE spaces:

fields = {
<name> : (<data_type>, <shape>, <region_name>, <approx_order>)
}

where
β€’ <data_type> is a numpy type (float64 or complex128) or β€˜real’ or β€˜complex’
β€’ <shape> is the number of DOFs per node: 1 or (1,) or β€˜scalar’, space dimension (2, or (2,) or 3 or (3,)) or
β€˜vector’; it can be other positive integer than just 1, 2, or 3
β€’ <region_name> is the name of region where the field is defined
β€’ <approx_order> is the FE approximation order, e.g. 0, 1, 2, β€˜1B’ (1 with bubble)
Example: scalar P1 elements in 2D on a region Omega:

fields = {
'temperature' : ('real', 1, 'Omega', 1),
}

The following approximation orders can be used:


β€’ simplex elements: 1, 2, β€˜1B’, β€˜2B’
β€’ tensor product elements: 0, 1, β€˜1B’
Optional bubble function enrichment is marked by β€˜B’.

38 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

Variables

Variables use the FE approximation given by the specified field:

variables = {
<name> : (<kind>, <field_name>, <spec>, [<history>])
}

where
β€’ <kind> - β€˜unknown field’, β€˜test field’ or β€˜parameter field’
β€’ <spec> - in case of: primary variable - order in the global vector of unknowns, dual variable - name of
primary variable
β€’ <history> - number of time steps to remember prior to current step
Example:

variables = {
't' : ('unknown field', 'temperature', 0, 1),
's' : ('test field', 'temperature', 't'),
}

Integrals

Define the integral type and quadrature rule. This keyword is optional, as the integration orders can be specified directly
in equations (see below):

integrals = {
<name> : <order>
}

where
β€’ <name> - the integral name - it has to begin with β€˜i’!
β€’ <order> - the order of polynomials to integrate, or β€˜custom’ for integrals with explicitly given values and
weights
Example:

import numpy as nm
N = 2
integrals = {
'i1' : 2,
'i2' : ('custom', zip(nm.linspace( 1e-10, 0.5, N ),
nm.linspace( 1e-10, 0.5, N )),
[1./N] * N),
}

1.4. User’s Guide 39


SfePy Documentation, Release version: 2022.1+git.f9873484

Essential Boundary Conditions and Constraints

The essential boundary conditions set values of DOFs in some regions, while the constraints constrain or transform
values of DOFs in some regions.

Dirichlet Boundary Conditions

The Dirichlet, or essential, boundary conditions apply in a given region given by its name, and, optionally, in selected
times. The times can be given either using a list of tuples (t0, t1) making the condition active for t0 <= t < t1, or by a
name of a function taking the time argument and returning True or False depending on whether the condition is active
at the given time or not.
Dirichlet (essential) boundary conditions:

ebcs = {
<name> : (<region_name>, [<times_specification>,]
{<dof_specification> : <value>[,
<dof_specification> : <value>, ...]})
}

Example:

ebcs = {
'u1' : ('Left', {'u.all' : 0.0}),
'u2' : ('Right', [(0.0, 1.0)], {'u.0' : 0.1}),
'phi' : ('Surface', {'phi.all' : 0.0}),
'u_yz' : ('Gamma', {'u.[1,2]' : 'rotate_yz'}),
}

The u_yz condition illustrates calculating the condition value by a function. In this example, it is a function of coordi-
nates coors of region nodes:

import numpy as nm

def rotate_yz(ts, coor, **kwargs):


from sfepy.linalg import rotation_matrix2d

vec = coor[:,1:3] - centre

angle = 10.0 * ts.step

mtx = rotation_matrix2d(angle)
vec_rotated = nm.dot(vec, mtx)

displacement = vec_rotated - vec

return displacement

The function needs to be registered in Functions:

functions = {
'rotate_yz' : (rotate_yz,),
}

40 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

Periodic Boundary Conditions

The periodic boundary conditions tie DOFs of a single variable in two regions that have matching nodes. Can be used
with functions in sfepy.discrete.fem.periodic.
Periodic boundary conditions:

epbcs = {
<name> : ((<region1_name>, <region2_name>), [<times_specification>,]
{<dof_specification> : <dof_specification>[,
<dof_specification> : <dof_specification>, ...]},
<match_function_name>)
}

Example:

epbcs = {
'up1' : (('Left', 'Right'), {'u.all' : 'u.all', 'p.0' : 'p.0'},
'match_y_line'),
}

Linear Combination Boundary Conditions

The linear combination boundary conditions (LCBCs) are more general than the Dirichlet BCs or periodic BCs. They
can be used to substitute one set of DOFs in a region by another set of DOFs, possibly in another region and of another
variable. The LCBCs can be used only in FEM with nodal (Lagrange) basis.
Available LCBC kinds:
β€’ 'rigid' - in linear elasticity problems, a region moves as a rigid body;
β€’ 'no_penetration' - in flow problems, the velocity vector is constrained to the plane tangent to the surface;
β€’ 'normal_direction' - the velocity vector is constrained to the normal direction;
β€’ 'edge_direction' - the velocity vector is constrained to the mesh edge direction;
β€’ 'integral_mean_value' - all DOFs in a region are summed to a single new DOF;
β€’ 'shifted_periodic' - generalized periodic BCs that work with two different variables and can have a non-zero
mutual shift.
Only the 'shifted_periodic' LCBC needs the second region and the DOF mapping function, see below.
Linear combination boundary conditions:

lcbcs = {
'shifted' : (('Left', 'Right'),
{'u1.all' : 'u2.all'},
'match_y_line', 'shifted_periodic',
'get_shift'),
'mean' : ('Middle', {'u1.all' : None}, None, 'integral_mean_value'),
}

1.4. User’s Guide 41


SfePy Documentation, Release version: 2022.1+git.f9873484

Initial Conditions

Initial conditions are applied prior to the boundary conditions - no special care must be used for the boundary dofs:

ics = {
<name> : (<region_name>, {<dof_specification> : <value>[,
<dof_specification> : <value>, ...]},...)
}

Example:

ics = {
'ic' : ('Omega', {'T.0' : 5.0}),
}

Materials

Materials are used to define constitutive parameters (e.g. stiffness, permeability, or viscosity), and other non-field
arguments of terms (e.g. known traction or volume forces). Depending on a particular term, the parameters can be
constants, functions defined over FE mesh nodes, functions defined in the elements, etc.
Example:

material = {
'm' : ({'val' : [0.0, -1.0, 0.0]},),
'm2' : 'get_pars',
'm3' : (None, 'get_pars'), # Same as the above line.
}

Example: different material parameters in regions β€˜Yc’, β€˜Ym’:

from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson


dim = 3
materials = {
'mat' : ({'D' : {
'Ym': stiffness_from_youngpoisson(dim, 7.0e9, 0.4),
'Yc': stiffness_from_youngpoisson(dim, 70.0e9, 0.2)}
},),
}

Defining Material Parameters by Functions

The functions for defining material parameters can work in two modes, distinguished by the mode argument. The two
modes are β€˜qp’ and β€˜special’. The first mode is used for usual functions that define parameters in quadrature points
(hence β€˜qp’), while the second one can be used for special values like various flags.
The shape and type of data returned in the β€˜special’ mode can be arbitrary (depending on the term used). On the other
hand, in the β€˜qp’ mode all the data have to be numpy float64 arrays with shape (n_coor, n_row, n_col), where n_coor
is the number of quadrature points given by the coors argument, n_coor = coors.shape[0], and (n_row, n_col) is the
shape of a material parameter in each quadrature point. For example, for scalar parameters, the shape is (n_coor, 1, 1).
The shape is determined by each term.
Example:

42 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

def get_pars(ts, coors, mode=None, **kwargs):


if mode == 'qp':
val = coors[:,0]
val.shape = (coors.shape[0], 1, 1)

return {'x_coor' : val}

The function needs to be registered in Functions:

functions = {
'get_pars' : (get_pars,),
}

If a material parameter has the same value in all quadrature points, than it is not necessary to repeat the constant and
the array can be with shape (1, n_row, n_col).

Equations and Terms

Equations can be built by combining terms listed in Term Table.

Examples

β€’ Laplace equation, named integral:

equations = {
'Temperature' : """dw_laplace.i.Omega( coef.val, s, t ) = 0"""
}

β€’ Laplace equation, simplified integral given by order:

equations = {
'Temperature' : """dw_laplace.2.Omega( coef.val, s, t ) = 0"""
}

β€’ Laplace equation, automatic integration order (not implemented yet!):

equations = {
'Temperature' : """dw_laplace.a.Omega( coef.val, s, t ) = 0"""
}

β€’ Navier-Stokes equations:

equations = {
'balance' :
"""+ dw_div_grad.i2.Omega( fluid.viscosity, v, u )
+ dw_convect.i2.Omega( v, u )
- dw_stokes.i1.Omega( v, p ) = 0""",
'incompressibility' :
"""dw_stokes.i1.Omega( u, q ) = 0""",
}

1.4. User’s Guide 43


SfePy Documentation, Release version: 2022.1+git.f9873484

Configuring Solvers

In SfePy, a non-linear solver has to be specified even when solving a linear problem. The linear problem is/should be
then solved in one iteration of the nonlinear solver.
Linear and nonlinear solver:

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton',
{'i_max' : 1}),
}

Solver selection:

options = {
'nls' : 'newton',
'ls' : 'ls',
}

For the case that a chosen linear solver is not available, it is possible to define the fallback option of the chosen solver
which specifies a possible alternative:

solvers = {
'ls': ('ls.mumps', {'fallback': 'ls2'}),
'ls2': ('ls.scipy_umfpack', {}),
'newton': ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
}),
}

Another possibility is to use a β€œvirtual” solver that ensures an automatic selection of an available solver, see Virtual
Linear Solvers with Automatic Selection.

Functions

Functions are a way of customizing SfePy behavior. They make it possible to define material properties, boundary
conditions, parametric sweeps, and other items in an arbitrary manner. Functions are normal Python functions declared
in the Problem Definition file, so they can invoke the full power of Python. In order for SfePy to make use of the
functions, they must be declared using the function keyword. See the examples below, and also the corresponding
sections above.

Examples

See sfepy/examples/diffusion/poisson_functions.py for a complete problem description file demonstrating


how to use different kinds of functions.
β€’ functions for defining regions:

def get_circle(coors, domain=None):


r = nm.sqrt(coors[:,0]**2.0 + coors[:,1]**2.0)
return nm.where(r < 0.2)[0]
(continues on next page)

44 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

functions = {
'get_circle' : (get_circle,),
}

β€’ functions for defining boundary conditions:

def get_p_edge(ts, coors, bc=None, problem=None):


if bc.name == 'p_left':
return nm.sin(nm.pi * coors[:,1])
else:
return nm.cos(nm.pi * coors[:,1])

functions = {
'get_p_edge' : (get_p_edge,),
}

ebcs = {
'p' : ('Gamma', {'p.0' : 'get_p_edge'}),
}

The values can be given by a function of time, coordinates and possibly other data, for example:

ebcs = {
'f1' : ('Gamma1', {'u.0' : 'get_ebc_x'}),
'f2' : ('Gamma2', {'u.all' : 'get_ebc_all'}),
}

def get_ebc_x(coors, amplitude):


z = coors[:, 2]
val = amplitude * nm.sin(z * 2.0 * nm.pi)
return val

def get_ebc_all(ts, coors):


val = ts.step * coors
return val

functions = {
'get_ebc_x' : (lambda ts, coors, bc, problem, **kwargs:
get_ebc_x(coors, 5.0),),
'get_ebc_all' : (lambda ts, coors, bc, problem, **kwargs:
get_ebc_all(ts, coors),),
}

Note that when setting more than one component as in get_ebc_all() above, the function should return either
an array of shape (coors.shape[0], n_components), or the same array flattened to 1D row-by-row (i.e. node-by-
node), where n_components corresponds to the number of components in the boundary condition definition. For
example, with β€˜u.[0, 1]’, n_components is 2.
β€’ function for defining usual material parameters:

def get_pars(ts, coors, mode=None, **kwargs):


if mode == 'qp':
(continues on next page)

1.4. User’s Guide 45


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


val = coors[:,0]
val.shape = (coors.shape[0], 1, 1)

return {'x_coor' : val}

functions = {
'get_pars' : (get_pars,),
}

The keyword arguments contain both additional use-specified arguments, if any, and the following: equations,
term, problem, for cases when the function needs access to the equations, problem, or term instances that
requested the parameters that are being evaluated. The full signature of the function is:
def get_pars(ts, coors, mode=None,
equations=None, term=None, problem=None, **kwargs)

β€’ function for defining special material parameters, with an extra argument:


def get_pars_special(ts, coors, mode=None, extra_arg=None):
if mode == 'special':
if extra_arg == 'hello!':
ic = 0
else:
ic = 1
return {('x_%s' % ic) : coors[:,ic]}

functions = {
'get_pars1' : (lambda ts, coors, mode=None, **kwargs:
get_pars_special(ts, coors, mode,
extra_arg='hello!'),),
}

# Just another way of adding a function, besides 'functions' keyword.


function_1 = {
'name' : 'get_pars2',
'function' : lambda ts, coors, mode=None, **kwargs:
get_pars_special(ts, coors, mode, extra_arg='hi!'),
}

β€’ function combining both kinds of material parameters:


def get_pars_both(ts, coors, mode=None, **kwargs):
out = {}

if mode == 'special':

out['flag'] = coors.max() > 1.0

elif mode == 'qp':

val = coors[:,1]
val.shape = (coors.shape[0], 1, 1)

(continues on next page)

46 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


out['y_coor'] = val

return out

functions = {
'get_pars_both' : (get_pars_both,),
}

β€’ function for setting values of a parameter variable:


variable_1 = {
'name' : 'p',
'kind' : 'parameter field',
'field' : 'temperature',
'like' : None,
'special' : {'setter' : 'get_load_variable'},
}

def get_load_variable(ts, coors, region=None):


y = coors[:,1]
val = 5e5 * y
return val

functions = {
'get_load_variable' : (get_load_variable,)
}

Miscellaneous

The options can be used to select solvers, output file format, output directory, to register functions to be called at various
phases of the solution (the hooks), and for other settings.
Additional options (including solver selection):
options = {
# int >= 0, uniform mesh refinement level
'refinement_level : 0',

# bool, default: False, if True, allow selecting empty regions with no


# entities
'allow_empty_regions' : True,

# string, output directory


'output_dir' : 'output/<output_dir>',

# 'vtk' or 'h5', output file (results) format


'output_format' : 'h5',

# string, nonlinear solver name


'nls' : 'newton',

# string, linear solver name


(continues on next page)

1.4. User’s Guide 47


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'ls' : 'ls',

# string, time stepping solver name


'ts' : 'ts',

# The times at which results should be saved:


# - a sequence of times
# - or 'all' for all time steps (the default value)
# - or an int, number of time steps, spaced regularly from t0 to t1
# - or a function `is_save(ts)`
'save_times' : 'all',

# save a restart file for each time step, only the last computed time
# step restart file is kept.
'save_restart' : -1,

# string, a function to be called after each time step


'step_hook' : '<step_hook_function>',

# string, a function to be called after each time step, used to


# update the results to be saved
'post_process_hook' : '<post_process_hook_function>',

# string, as above, at the end of simulation


'post_process_hook_final' : '<post_process_hook_final_function>',

# string, a function to generate probe instances


'gen_probes' : '<gen_probes_function>',

# string, a function to probe data


'probe_hook' : '<probe_hook_function>',

# string, a function to modify problem definition parameters


'parametric_hook' : '<parametric_hook_function>',

# float, default: 1e-9. If the distance between two mesh vertices


# is less than this value, they are considered identical.
# This affects:
# - periodic regions matching
# - mirror regions matching
# - fixing of mesh doubled vertices
'mesh_eps': 1e-7,

# bool, default: True. If True, the (tangent) matrices and residual


# vectors (right-hand sides) contain only active DOFs, otherwise all
# DOFs (including the ones fixed by the Dirichlet or periodic boundary
# conditions) are included. Note that the rows/columns corresponding to
# fixed DOFs are modified w.r.t. a problem without the boundary
# conditions.
'active_only' : False,
}

β€’ post_process_hook enables computing derived quantities, like stress or strain, from the primary unknown

48 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

variables. See the examples in sfepy/examples/large_deformation/ directory.


β€’ parametric_hook makes it possible to run parametric studies by modifying the problem description program-
matically. See sfepy/examples/diffusion/poisson_parametric_study.py for an example.
β€’ output_dir redirects output files to specified directory

1.4.4 Building Equations in SfePy

Equations in SfePy are built using terms, which correspond directly to the integral forms of weak formulation of a
problem to be solved. As an example, let us consider the Laplace equation in time interval 𝑑 ∈ [0, 𝑑final ]:
πœ•π‘‡
+ π‘βˆ†π‘‡ = 0 in Ω, 𝑇 (𝑑) = 𝑇¯(𝑑) on Ξ“ . (1.5)
πœ•π‘‘
The weak formulation of (1.5) is: Find 𝑇 ∈ 𝑉 , such that
∫︁ ∫︁
πœ•π‘‡
𝑠 + 𝑐 βˆ‡π‘‡ : βˆ‡π‘  = 0, βˆ€π‘  ∈ 𝑉0 , (1.6)
Ξ© πœ•π‘‘ Ξ©

where we assume no fluxes over πœ•β„¦ βˆ– Ξ“. In the syntax used in SfePy input files, this can be written as:

dw_dot.i.Omega( s, dT/dt ) + dw_laplace.i.Omega( coef, s, T) = 0

which directly corresponds to the discrete version of (1.6): Find 𝑇 ∈ π‘‰β„Ž , such that
∫︁ ∫︁
πœ•π‘‡
𝑠𝑇 ( πœ‘π‘‡ πœ‘) + 𝑠𝑇 ( 𝑐 𝐺𝑇 𝐺)𝑇 = 0, βˆ€π‘  ∈ π‘‰β„Ž0 ,
Ξ©β„Ž πœ•π‘‘ Ξ©β„Ž

where 𝑒 β‰ˆ πœ‘π‘’, βˆ‡π‘’ β‰ˆ 𝐺𝑒 for 𝑒 ∈ {𝑠, 𝑇 }. The integrals over the discrete domain β„¦β„Ž are approximated by a numerical
quadrature, that is named i in our case.

Syntax of Terms in Equations

The terms in equations are written in form:

<term_name>.<i>.<r>( <arg1>, <arg2>, ... )

where <i> denotes an integral name (i.e. a name of numerical quadrature to use) and <r> marks a region (domain
of the integral). In the following, <virtual> corresponds to a test function, <state> to a unknown function and
<parameter> to a known function arguments.
When solving, the individual terms in equations are evaluated in the β€˜weak’ mode. The evaluation modes are described
in the next section.

1.4.5 Term Evaluation

Terms can be evaluated in two ways:


1. implicitly by using them in equations;
2. explicitly using Problem.evaluate(). This way is mostly used in the postprocessing.
Each term supports one or more evaluation modes:
β€’ β€˜weak’ : Assemble (in the finite element sense) either the vector or matrix depending on diff_var argument (the
name of variable to differentiate with respect to) of Term.evaluate(). This mode is usually used implicitly
when building the linear system corresponding to given equations.

1.4. User’s Guide 49


SfePy Documentation, Release version: 2022.1+git.f9873484

β€’ β€˜eval’ : The evaluation mode integrates the term (= integral) over a region. The result has the same dimension
as the quantity being integrated. This mode can be used, for example, to compute some global quantities during
postprocessing such as fluxes or total values of extensive quantities (mass, volume, energy, . . . ).
β€’ β€˜el_eval’ : The element evaluation mode results in an array of a quantity integrated over each element of a region.
β€’ β€˜el_avg’ : The element average mode results in an array of a quantity averaged in each element of a region. This
is the mode for postprocessing.
β€’ β€˜qp’ : The quadrature points mode results in an array of a quantity interpolated into quadrature points of each
element in a region. This mode is used when further point-wise calculations with the result are needed. The
same element type and number of quadrature points in each element are assumed.
Not all terms support all the modes - consult the documentation of the individual terms. There are, however, certain
naming conventions:
β€’ β€˜dw_*’ terms support β€˜weak’ mode
β€’ β€˜dq_*’ terms support β€˜qp’ mode
β€’ β€˜d_*’, β€˜di_*’ terms support β€˜eval’ and β€˜el_eval’ modes
β€’ β€˜ev_*’ terms support β€˜eval’, β€˜el_eval’, β€˜el_avg’ and β€˜qp’ modes
Note that the naming prefixes are due to history when the mode argument to Problem.evaluate() and Term.
evaluate() was not available. Now they are often redundant, but are kept around to indicate the evaluation purpose
of each term.
Several examples of using the Problem.evaluate() function are shown below.

1.4.6 Solution Postprocessing

A solution to equations given in a problem description file is given by the variables of the β€˜unknown field’ kind, that
are set in the solution procedure. By default, those are the only values that are stored into a results file. The solution
postprocessing allows computing additional, derived, quantities, based on the primary variables values, as well as any
other quantities to be stored in the results.
Let us illustrate this using several typical examples. Let us assume that the postprocessing function is
called β€˜post_process()’, and is added to options as discussed in Miscellaneous, see β€˜post_process_hook’ and
β€˜post_process_hook_final’. Then:
β€’ compute stress and strain given the displacements (variable u):

def post_process(out, problem, variables, extend=False):


"""
This will be called after the problem is solved.

Parameters
----------
out : dict
The output dictionary, where this function will store additional
data.
problem : Problem instance
The current Problem instance.
variables : Variables instance
The computed state, containing FE coefficients of all the unknown
variables.
extend : bool
(continues on next page)

50 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


The flag indicating whether to extend the output data to the whole
domain. It can be ignored if the problem is solved on the whole
domain already.

Returns
-------
out : dict
The updated output dictionary.
"""
from sfepy.base.base import Struct

# Cauchy strain averaged in elements.


strain = problem.evaluate('ev_cauchy_strain.i.Omega(u)',
mode='el_avg')
out['cauchy_strain'] = Struct(name='output_data',
mode='cell', data=strain,
dofs=None)
# Cauchy stress averaged in elements.
stress = problem.evaluate('ev_cauchy_stress.i.Omega(solid.D, u)',
mode='el_avg')
out['cauchy_stress'] = Struct(name='output_data',
mode='cell', data=stress,
dofs=None)

return out

The full example is linear_elasticity-linear_elastic_probes.


β€’ compute diffusion velocity given the pressure:

def post_process(out, pb, state, extend=False):


from sfepy.base.base import Struct

dvel = pb.evaluate('ev_diffusion_velocity.i.Omega(m.K, p)',


mode='el_avg')
out['dvel'] = Struct(name='output_data',
mode='cell', data=dvel, dofs=None)

return out

The full example is biot-biot_npbc.


β€’ store values of a non-homogeneous material parameter:

def post_process(out, pb, state, extend=False):


from sfepy.base.base import Struct

mu = pb.evaluate('ev_integrate_mat.2.Omega(nonlinear.mu, u)',
mode='el_avg', copy_materials=False, verbose=False)
out['mu'] = Struct(name='mu', mode='cell', data=mu, dofs=None)

return out

The full example is linear_elasticity/material_nonlinearity.py.

1.4. User’s Guide 51


SfePy Documentation, Release version: 2022.1+git.f9873484

β€’ compute volume of a region (u is any variable defined in the region Omega):

volume = problem.evaluate('ev_volume.2.Omega(u)')

1.4.7 Probing

Probing applies interpolation to output the solution along specified paths. There are two ways of probing:
β€’ VTK probes: It is the simple way of probing using the β€˜post_process_hook’. It generates matplotlib figures
with the probing results and previews of the mesh with the probe paths. See Primer or linear_elasticity-its2D_5
example.
β€’ SfePy probes: As mentioned in Miscellaneous, it relies on defining two additional functions, namely the
β€˜gen_probes’ function, that should create the required probes (see sfepy.discrete.probes), and the
β€˜probe_hook’ function that performs the actual probing of the results for each of the probes. This function can re-
turn the probing results, as well as a handle to a corresponding matplotlib figure. See linear_elasticity/its2D_4.py
for additional explanation.
Using sfepy.discrete.probes allows correct probing of fields with the approximation order greater than one,
see Interactive Example in Primer or linear_elasticity/its2D_interactive.py for an example of interactive use.

1.4.8 Postprocessing filters

The following postprocessing functions based on the VTK filters are available:
β€’ β€˜get_vtk_surface’: extract mesh surface
β€’ β€˜get_vtk_edges’: extract mesh edges
β€’ β€˜get_vtk_by_group’: extract domain by a material ID
β€’ β€˜tetrahedralize_vtk_mesh’: 3D cells are converted to tetrahedral meshes, 2D cells to triangles
The following code demonstrates the use of the postprocessing filters:

mesh = problem.domain.mesh
mesh_name = mesh.name[mesh.name.rfind(osp.sep) + 1:]

vtkdata = get_vtk_from_mesh(mesh, out, 'postproc_')


matrix = get_vtk_by_group(vtkdata, 1, 1)

matrix_surf = get_vtk_surface(matrix)
matrix_surf_tri = tetrahedralize_vtk_mesh(matrix_surf)
write_vtk_to_file('%s_mat1_surface.vtk' % mesh_name, matrix_surf_tri)

matrix_edges = get_vtk_edges(matrix)
write_vtk_to_file('%s_mat1_edges.vtk' % mesh_name, matrix_edges)

52 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

1.4.9 Solvers

This section describes the time-stepping, nonlinear, linear, eigenvalue and optimization solvers available in SfePy.
There are many internal and external solvers in the sfepy.solvers package that can be called using a uniform interface.

Time-stepping solvers

All PDEs that can be described in a problem description file are solved internally by a time-stepping solver. This holds
even for stationary problems, where the default single-step solver ('ts.stationary') is created automatically. In this
way, all problems are treated in a uniform way. The same holds when building a problem interactively, or when writing
a script, whenever the Problem.solve() function is used for a problem solution.
The following solvers are available:
β€’ ts.adaptive: Implicit time stepping solver with an adaptive time step.
β€’ ts.bathe: Solve elastodynamics problems by the Bathe method.
β€’ ts.euler: Simple forward euler method
β€’ ts.generalized_alpha: Solve elastodynamics problems by the generalized 𝛼 method.
β€’ ts.multistaged: Explicit time stepping solver with multistage solve_step method
β€’ ts.newmark: Solve elastodynamics problems by the Newmark method.
β€’ ts.runge_kutta_4: Classical 4th order Runge-Kutta method,
β€’ ts.simple: Implicit time stepping solver with a fixed time step.
β€’ ts.stationary: Solver for stationary problems without time stepping.
β€’ ts.tvd_runge_kutta_3: 3rd order Total Variation Diminishing Runge-Kutta method
β€’ ts.velocity_verlet: Solve elastodynamics problems by the velocity-Verlet method.
See sfepy.solvers.ts_solvers for available time-stepping solvers and their options.

Nonlinear Solvers

Almost every problem, even linear, is solved in SfePy using a nonlinear solver that calls a linear solver in each iteration.
This approach unifies treatment of linear and non-linear problems, and simplifies application of Dirichlet (essential)
boundary conditions, as the linear system computes not a solution, but a solution increment, i.e., it always has zero
boundary conditions.
The following solvers are available:
β€’ nls.newton: Solves a nonlinear system 𝑓 (π‘₯) = 0 using the Newton method.
β€’ nls.oseen: The Oseen solver for Navier-Stokes equations.
β€’ nls.petsc: Interface to PETSc SNES (Scalable Nonlinear Equations Solvers).
β€’ nls.scipy_broyden_like: Interface to Broyden and Anderson solvers from scipy.optimize.
β€’ nls.semismooth_newton: The semi-smooth Newton method.
See sfepy.solvers.nls, sfepy.solvers.oseen and sfepy.solvers.semismooth_newton for all available
nonlinear solvers and their options.

1.4. User’s Guide 53


SfePy Documentation, Release version: 2022.1+git.f9873484

Linear Solvers

Choosing a suitable linear solver is key to solving efficiently stationary as well as transient PDEs. SfePy allows using
a number of external solvers with a unified interface.
The following solvers are available:
β€’ ls.cm_pb: Conjugate multiple problems.
β€’ ls.mumps: Interface to MUMPS solver.
β€’ ls.mumps_par: Interface to MUMPS parallel solver.
β€’ ls.petsc: PETSc Krylov subspace solver.
β€’ ls.pyamg: Interface to PyAMG solvers.
β€’ ls.pyamg_krylov: Interface to PyAMG Krylov solvers.
β€’ ls.schur_mumps: Mumps Schur complement solver.
β€’ ls.scipy_direct: Direct sparse solver from SciPy.
β€’ ls.scipy_iterative: Interface to SciPy iterative solvers.
β€’ ls.scipy_superlu: SuperLU - direct sparse solver from SciPy.
β€’ ls.scipy_umfpack: UMFPACK - direct sparse solver from SciPy.
See sfepy.solvers.ls for all available linear solvers and their options.

Virtual Linear Solvers with Automatic Selection

A β€œvirtual” solver can be used in case it is not clear which external linear solvers are available. Each β€œvirtual” solver
selects the first available solver from a pre-defined list.
The following solvers are available:
β€’ ls.auto_direct: The automatically selected linear direct solver.
β€’ ls.auto_iterative: The automatically selected linear iterative solver.
See sfepy.solvers.auto_fallback for all available virtual solvers.

Eigenvalue Problem Solvers

The following eigenvalue problem solvers are available:


β€’ eig.matlab: Matlab eigenvalue problem solver.
β€’ eig.scipy: SciPy-based solver for both dense and sparse problems.
β€’ eig.scipy_lobpcg: SciPy-based LOBPCG solver for sparse symmetric problems.
β€’ eig.sgscipy: SciPy-based solver for dense symmetric problems.
β€’ eig.slepc: General SLEPc eigenvalue problem solver.
See sfepy.solvers.eigen for available eigenvalue problem solvers and their options.

54 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

Quadratic Eigenvalue Problem Solvers

The following quadratic eigenvalue problem solvers are available:


β€’ eig.qevp: Quadratic eigenvalue problem solver based on the problem linearization.
See sfepy.solvers.qeigen for available quadratic eigenvalue problem solvers and their options.

Optimization Solvers

The following optimization solvers are available:


β€’ nls.scipy_fmin_like: Interface to SciPy optimization solvers scipy.optimize.fmin_*.
β€’ opt.fmin_sd: Steepest descent optimization solver.
See sfepy.solvers.optimize for available optimization solvers and their options.

1.4.10 Solving Problems in Parallel

The PETSc-based nonlinear equations solver 'nls.petsc' and linear system solver 'ls.petsc' can be used for
parallel computations, together with the modules in sfepy.parallel package. This feature is very preliminary, and can
be used only with the commands for interactive use - problem description files are not supported (yet). The key module
is sfepy.parallel.parallel that takes care of the domain and field DOFs distribution among parallel tasks, as well
as parallel assembling to PETSc vectors and matrices.

Current Implementation Drawbacks

β€’ The partitioning of the domain and fields DOFs is not done in parallel and all tasks need to load the whole mesh
and define the global fields - those must fit into memory available to each task.
β€’ While all KSP and SNES solver are supported, in principle, most of their options have to be passed using the
command-line parameters of PETSc - they are not supported yet in the SfePy solver parameters.
β€’ There are no performance statistics yet. The code was tested on a single multi-cpu machine only.
β€’ The global solution is gathered to task 0 and saved to disk serially.
β€’ The vertices of surface region selector does not work in parallel, because the region definition is applied
to a task-local domain.

Examples

The examples demonstrating the use parallel problem solving in SfePy are:
β€’ diffusion/poisson_parallel_interactive.py
β€’ multi_physics/biot_parallel_interactive.py
See their help messages for further information.

1.4. User’s Guide 55


SfePy Documentation, Release version: 2022.1+git.f9873484

1.4.11 Isogeometric Analysis

Isogeometric analysis (IGA) is a recently developed computational approach that allows using the NURBS-based do-
main description from CAD design tools also for approximation purposes similar to the finite element method.
The implementation is SfePy is based on Bezier extraction of NURBS as developed in1 . This approach allows reusing
the existing finite element assembling routines, as still the evaluation of weak forms occurs locally in β€œelements” and
the local contributions are then assembled to the global system.

Current Implementation

The IGA code is still very preliminary and some crucial components are missing. The current implementation is also
very slow, as it is in pure Python.
The following already works:
β€’ single patch tensor product domain support in 2D and 3D
β€’ region selection based on topological Bezier mesh, see below
β€’ Dirichlet boundary conditions using projections for non-constant values
β€’ evaluation in arbitrary point in the physical domain
β€’ both scalar and vector volume terms work
β€’ term integration over the whole domain as well as a volume subdomain
β€’ simple linearization (output file generation) based on sampling the results with uniform parametric vectors
β€’ basic domain generation with script/gen_iga_patch.py based on igakit
The following is not implemented yet:
β€’ tests
β€’ theoretical convergence rate verification
β€’ surface terms
β€’ other boundary conditions
β€’ proper (adaptive) linearization for post-processing
β€’ support for multiple NURBS patches

Domain Description

The domain description is in custom HDF5-based files with .iga extension. Such a file contains:
β€’ NURBS patch data (knots, degrees, control points and weights). Those can either be generated using igakit,
created manually or imported from other tools.
β€’ Bezier extraction operators and corresponding DOF connectivity (computed by SfePy).
β€’ Bezier mesh control points, weights and connectivity (computed by SfePy).
The Bezier mesh is used to create a topological Bezier mesh - a subset of the Bezier mesh containing the Bezier
element corner vertices only. Those vertices are interpolatory (are on the exact geometry) and so can be used for region
selections.
1 Michael J. Borden, Michael A. Scott, John A. Evans, Thomas J. R. Hughes: Isogeometric finite element data structures based on Bezier

extraction of NURBS, Institute for Computational Engineering and Sciences, The University of Texas at Austin, Austin, Texas, March 2010.

56 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

Region Selection

The domain description files contain vertex sets for regions corresponding to the patch sides, named 'xiIJ', where I
is the parametric axis (0, 1, or 2) and J is 0 or 1 for the beginning and end of the axis knot span. Other regions can be
defined in the usual way, using the topological Bezier mesh entities.

Examples

The examples demonstrating the use of IGA in SfePy are:


β€’ diffusion/poisson_iga.py
β€’ linear_elasticity/linear_elastic_iga.py
β€’ navier_stokes/navier_stokes2d_iga.py
Their problem description files are almost the same as their FEM equivalents, with the following differences:
β€’ There is filename_domain instead of filename_mesh.
β€’ Fields are defined as follows:

fields = {
't1' : ('real', 1, 'Omega', None, 'H1', 'iga'),
't2' : ('real', 1, 'Omega', 'iga', 'H1', 'iga'),
't3' : ('real', 1, 'Omega', 'iga+%d', 'H1', 'iga'),
}

The approximation order in the first definition is None as it is given by the NURBS degrees in the domain
description. The second definition is equivalent to the first one. The third definition, where %d should be a non-
negative integer, illustrates how to increase the field’s NURBS degrees (while keeping the continuity) w.r.t. the
domain NURBS description. It is applied in the navier_stokes/navier_stokes2d_iga.py example to the velocity
field.

1.5 Examples

This section contains domain-specific tutorials as well as the automatically generated list of the standard examples that
come with SfePy.

1.5.1 Primer

A beginner’s tutorial highlighting the basics of SfePy.

Introduction

This primer presents a step-by-step walk-through of the process to solve a simple mechanics problem. The typical
process to solve a problem using SfePy is followed: a model is meshed, a problem definition file is drafted, SfePy is run
to solve the problem and finally the results of the analysis are visualised.

1.5. Examples 57
SfePy Documentation, Release version: 2022.1+git.f9873484

Problem statement

A popular test to measure the tensile strength of concrete or asphalt materials is the indirect tensile strength (ITS) test
pictured below. In this test a cylindrical specimen is loaded across its diameter to failure. The test is usually run by
loading the specimen at a constant deformation rate of 50 mm/minute (say) and measuring the load response. When
the tensile stress that develops in the specimen under loading exceeds its tensile strength then the specimen will fail.
To model this problem using finite elements the indirect tensile test can be simplified to represent a diametrically point
loaded disk as shown in the schematic.

The tensile and compressive stresses that develop in the specimen as a result of the point loads P are a function of the
diameter 𝐷 and thickness 𝑑 of the cylindrical specimen. At the centre of the specimen, the compressive stress is 3 times
the tensile stress and the analytical formulation for these are, respectively:

2𝑃
πœŽπ‘‘ = (1.7)
πœ‹π‘‘π·
6𝑃
πœŽπ‘ = (1.8)
πœ‹π‘‘π·
These solutions may be approximated using finite element methods. To solve this problem using SfePy the first step is
meshing a suitable model.

Meshing

Assuming plane strain conditions, the indirect tensile test may be modelled using a 2D finite element mesh. Further-
more, the geometry of the model is symmetrical about the x- and y-axes passing through the centre of the circle. To
take advantage of this symmetry only one quarter of the 2D model will be meshed and boundary conditions will be
established to indicate this symmetry. The meshing program Gmsh is used here to very quickly mesh the model. Follow
these steps to model the ITS:
1. The ITS specimen has a diameter of 150 mm. Using Gmsh add three new points (geometry elementary entities)
at the following coordinates: (0.00.0), (75.0, 0.0) and (0.0, 75.0).
2. Next add two straight lines connecting the points.

58 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

3. Next add a Circle arc connecting two of the points to form the quarter circle segment.
4. Still under Geometry add a ruled surface.
5. With the geometry of the model defined, add a mesh by clicking on the 2D button under the Mesh functions.
The figures that follow show the various stages in the model process.

That’s the meshing done. Save the mesh in a format that SfePy recognizes. For now use the medit .mesh format e.g.
its2D.mesh.
Hint: Check the drop down in the Save As dialog for the different formats that Gmsh can save to.

1.5. Examples 59
SfePy Documentation, Release version: 2022.1+git.f9873484

If you open the its2D.mesh file using a text editor you’ll notice that Gmsh saves the mesh in a 3D format and includes
some extra geometry items that should be deleted. Reformatted the mesh file to a 2D format and delete the Edges block.
Note that when you do this the file cannot be reopened by Gmsh so it is always a good idea to also save your meshes in
Gmsh’s native format as well (Shift-Ctrl-S). Click here to download the reformatted mesh file that will be used in the
tutorial.

You’ll notice that the mesh contains 55 vertices (nodes) and 83 triangle elements. The mesh file provides the coordinates
of the nodes and the element connectivity. It is important to note that node and element numbering in SfePy start at 0
and not 1 as is the case in Gmsh and some other meshing programs.
To view .mesh files you can use a demo of medit. After loading your mesh file with medit you can see the node and
element numbering by pressing P and F respectively. The numbering in medit starts at 1 as shown. Thus the node
at the center of the model in SfePy numbering is 0, and elements 76 and 77 are connected to this node. Node and
element numbers can also be viewed in Gmsh – under the mesh option under the Visibility tab enable the node and
surface labels. Note that the surface labels as numbered in Gmsh follow on from the line numbering. So to get the
corresponding element number in SfePy you’ll need to subtract the number of lines in the Gmsh file + 1. Confused yet?
Luckily, SfePy provides some useful mesh functions to indicate which elements are connected to which nodes. Nodes
and elements can also be identified by defining regions, which is addressed later.
Another open source python option to view .mesh files is the appropriately named Python Mesh Viewer.
The next step in the process is coding the SfePy problem definition file.

Problem description

The programming of the problem description file is well documented in the SfePy User’s Guide. The problem descrip-
tion file used in the tutorial follows:

r"""
Diametrically point loaded 2-D disk. See :ref:`sec-primer`.

Find :math:`\ul{u}` such that:

.. math::
\int_{\Omega} D_{ijkl}\ e_{ij}(\ul{v}) e_{kl}(\ul{u})
= 0
\;, \quad \forall \ul{v} \;,

(continues on next page)

60 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


where

.. math::
D_{ijkl} = \mu (\delta_{ik} \delta_{jl}+\delta_{il} \delta_{jk}) +
\lambda \ \delta_{ij} \delta_{kl}
\;.
"""
from __future__ import absolute_import
from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson
from sfepy.discrete.fem.utils import refine_mesh
from sfepy import data_dir

# Fix the mesh file name if you run this file outside the SfePy directory.
filename_mesh = data_dir + '/meshes/2d/its2D.mesh'

refinement_level = 0
filename_mesh = refine_mesh(filename_mesh, refinement_level)

output_dir = '.' # set this to a valid directory you have write access to

young = 2000.0 # Young's modulus [MPa]


poisson = 0.4 # Poisson's ratio

options = {
'output_dir' : output_dir,
}

regions = {
'Omega' : 'all',
'Left' : ('vertices in (x < 0.001)', 'facet'),
'Bottom' : ('vertices in (y < 0.001)', 'facet'),
'Top' : ('vertex 2', 'vertex'),
}

materials = {
'Asphalt' : ({'D': stiffness_from_youngpoisson(2, young, poisson)},),
'Load' : ({'.val' : [0.0, -1000.0]},),
}

fields = {
'displacement': ('real', 'vector', 'Omega', 1),
}

equations = {
'balance_of_forces' :
"""dw_lin_elastic.2.Omega(Asphalt.D, v, u)
= dw_point_load.0.Top(Load.val, v)""",
}

variables = {
'u' : ('unknown field', 'displacement', 0),
'v' : ('test field', 'displacement', 'u'),
(continues on next page)

1.5. Examples 61
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


}

ebcs = {
'XSym' : ('Bottom', {'u.1' : 0.0}),
'YSym' : ('Left', {'u.0' : 0.0}),
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-6,
}),
}

Download the Problem description file and open it in your favourite Python editor. Note that you may wish to
change the location of the output directory to somewhere on your drive. You may also need to edit the mesh file name.
For the analysis we will assume that the material of the test specimen is linear elastic and isotropic. We define two
material constants i.e. Young’s modulus and Poisson’s ratio. The material is assumed to be asphalt concrete having a
Young’s modulus of 2,000 MPa and a Poisson’s ration of 0.4.
Note: Be consistent in your choice and use of units. In the tutorial we are using Newton (N), millimeters (mm) and
megaPascal (MPa). The sfepy.mechanics.units module might help you in determining which derived units correspond
to given basic units.
The following block of code defines regions on your mesh:

regions = {
'Omega' : 'all',
'Left' : ('vertices in (x < 0.001)', 'facet'),
'Bottom' : ('vertices in (y < 0.001)', 'facet'),
'Top' : ('vertex 2', 'vertex'),
}

Four regions are defined:


1. β€˜Omega’: all the elements in the mesh,
2. β€˜Left’: the y-axis,
3. β€˜Bottom’: the x-axis,
4. β€˜Top’: the topmost node. This is where the load is applied.
Having defined the regions these can be used in other parts of your code. For example, in the definition of the boundary
conditions:

ebcs = {
'XSym' : ('Bottom', {'u.1' : 0.0}),
'YSym' : ('Left', {'u.0' : 0.0}),
}

Now the power of the regions entity becomes apparent. To ensure symmetry about the x-axis, the vertical or y-
displacement of the nodes in the β€˜Bottom’ region are prevented or set to zero. Similarly, for symmetry about the
y-axis, any horizontal or displacement in the x-direction of the nodes in the β€˜Left’ region or y-axis is prevented.
The load is specified in terms of the β€˜Load’ material as follows:

62 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

materials = {
'Asphalt' : ({
'lam' : lame_from_youngpoisson(young, poisson)[0],
'mu' : lame_from_youngpoisson(young, poisson)[1],
},),
'Load' : ({'.val' : [0.0, -1000.0]},),
}

Note the dot in β€˜.val’ – this denotes a special material value, i.e., a value that is not to be evaluated in quadrature points.
The load is then applied in equations using the β€˜dw_point_load.0.Top(Load.val, v)’ term in the topmost node (region
β€˜Top’).
We provided the material constants in terms of Young’s modulus and Poisson’s ratio, but the linear elastic isotropic
equation used requires as input Lamé’s parameters. The lame_from_youngpoisson() function is thus used for conver-
sion. Note that to use this function it was necessary to import the function into the code, which was done up front:

from sfepy.mechanics.matcoefs import lame_from_youngpoisson

Hint: Check out the sfepy.mechanics.matcoefs module for other useful material related functions.
That’s it – we are now ready to solve the problem.

Running SfePy

One option to solve the problem is to run the SfePy simple.py script from the command line:

./simple.py its2D_1.py

Note: For the purpose of this tutorial it is assumed that the problem description file (its2D_1.py) is in the same
directory as the simple.py script. If you have the its2D_1.py file in another directory then make sure you include the
path to this file as well.
SfePy solves the problem and outputs the solution to the output path (output_dir) provided in the script. The output file
will be in the VTK format by default if this is not explicitly specified and the name of the output file will be the same
as that used for the mesh file except with the β€˜.vtk’ extension i.e. its2D.vtk.
The VTK format is an ASCII format. Open the file using a text editor. You’ll notice that the output file includes separate
sections:
β€’ POINTS (these are the model nodes),
β€’ CELLS (the model element connectivity),
β€’ VECTORS (the node displacements in the x-, y- and z- directions).
SfePy provides a script (postproc.py) to quickly view the solution. To run this script you need to have Mayavi installed.
From the command line issue the following (assuming the correct paths):

./postproc.py its2D.vtk

The postproc.py script generates the image shown below, which shows by default the displacements in the model as
arrows and their magnitude as color scale. Cool, but we are more interested in the stresses. To get these we need to
modify the problem description file and do some post-processing.

1.5. Examples 63
SfePy Documentation, Release version: 2022.1+git.f9873484

Post-processing

SfePy provides functions to calculate stresses and strains. We’ll include a function to calculate these and update the
problem material definition and options to call this function as a post_process_hook(). Save this file as its2D_2.py.

r"""
Diametrically point loaded 2-D disk with postprocessing. See
:ref:`sec-primer`.

Find :math:`\ul{u}` such that:

.. math::
\int_{\Omega} D_{ijkl}\ e_{ij}(\ul{v}) e_{kl}(\ul{u})
= 0
\;, \quad \forall \ul{v} \;,

where

.. math::
D_{ijkl} = \mu (\delta_{ik} \delta_{jl}+\delta_{il} \delta_{jk}) +
\lambda \ \delta_{ij} \delta_{kl}
\;.
"""

from __future__ import absolute_import


from sfepy.examples.linear_elasticity.its2D_1 import *

from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson

def stress_strain(out, pb, state, extend=False):


"""
Calculate and output strain and stress for given displacements.
"""
from sfepy.base.base import Struct

ev = pb.evaluate
strain = ev('ev_cauchy_strain.2.Omega(u)', mode='el_avg')
stress = ev('ev_cauchy_stress.2.Omega(Asphalt.D, u)', mode='el_avg',
(continues on next page)

64 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


copy_materials=False)

out['cauchy_strain'] = Struct(name='output_data', mode='cell',


data=strain, dofs=None)
out['cauchy_stress'] = Struct(name='output_data', mode='cell',
data=stress, dofs=None)

return out

asphalt = materials['Asphalt'][0]
asphalt.update({'D' : stiffness_from_youngpoisson(2, young, poisson)})
options.update({'post_process_hook' : 'stress_strain',})

The updated file imports all of the previous definitions in its2D_1.py. The stress function (de_cauchy_stress())
requires as input the stiffness tensor – thus it was necessary to update the materials accordingly. The problem options
were also updated to call the stress_strain() function as a post_process_hook().
Run SfePy to solve the updated problem and view the solution (assuming the correct paths):

./simple.py its2D_2.py
./postproc.py its2D.vtk -b

In addition to the node displacements, the VTK output shown below now also includes the stresses and strains averaged
in the elements:

Remember the objective was to determine the stresses at the centre of the specimen under a load 𝑃 . The solution as
currently derived is expressed in terms of a global displacement vector 𝑒. The global (residual) force vector 𝑓 is a
function of the global displacement vector and the global stiffness matrix 𝐾 as: 𝑓 = 𝐾𝑒. Let’s determine the force
vector interactively.

1.5. Examples 65
SfePy Documentation, Release version: 2022.1+git.f9873484

Running SfePy in interactive mode

In addition to solving problems using the simple.py script you can also run SfePy interactively (we will use IPython
interactive shell in following examples).
In the SfePy top-level directory run

ipython

issue the following commands:

In [1]: from sfepy.applications import solve_pde

In [2]: pb, variables = solve_pde('its2D_2.py')

The problem is solved and the problem definition and solution are provided in the pb and variables variables respec-
tively. The solution, or in this case, the global displacement vector 𝑒, contains the x- and y-displacements at the nodes
in the 2D model:

In [3]: u = variables()

In [4]: u
Out[4]:
array([ 0. , 0. , 0.37376671, ..., -0.19923848,
0.08820237, -0.11201528])

In [5]: u.shape
Out[5]: (110,)

In [6]: u.shape = (55, 2)

In [7]: u
Out[7]:
array([[ 0. , 0. ],
[ 0.37376671, 0. ],
[ 0. , -1.65318152],
...,
[ 0.08716448, -0.23069047],
[ 0.27741356, -0.19923848],
[ 0.08820237, -0.11201528]])

Note: We have used the fact, that the state vector contains only one variable (u). In general, the following can be used:

In [8]: u = variables.get_state_parts()['u']

In [9]: u
Out[9]:
array([[ 0. , 0. ],
[ 0.37376671, 0. ],
[ 0. , -1.65318152],
...,
[ 0.08716448, -0.23069047],
[ 0.27741356, -0.19923848],
[ 0.08820237, -0.11201528]])

66 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

Both variables() and variables.get_state_parts() return a view of the DOF vector, that is why in Out[8] the vector is
reshaped according to Out[6]. It is thus possible to set the values of state variables by manipulating the state vector,
but shape changes such as the one above are not advised (see In [15] below) - work on a copy instead.
From the above it can be seen that u holds the displacements at the 55 nodes in the model and that the displacement
at node 2 (on which the load is applied) is (0, βˆ’1.65318152). The global stiffness matrix is saved in pb as a sparse
matrix:

In [10]: K = pb.mtx_a

In [11]: K
Out[11]:
<94x94 sparse matrix of type '<type 'numpy.float64'>'
with 1070 stored elements in Compressed Sparse Row format>

In [12]: print(K)
(0, 0) 2443.95959851
(0, 7) -2110.99917491
(0, 14) -332.960423597
(0, 15) 1428.57142857
(1, 1) 2443.95959852
(1, 13) -2110.99917492
(1, 32) 1428.57142857
(1, 33) -332.960423596
(2, 2) 4048.78343529
(2, 3) -1354.87004384
(2, 52) -609.367453538
(2, 53) -1869.0018791
(2, 92) -357.41672785
(2, 93) 1510.24654193
(3, 2) -1354.87004384
(3, 3) 4121.03202907
(3, 4) -1696.54911732
(3, 48) 76.2400806561
(3, 49) -1669.59247304
(3, 52) -1145.85294856
(3, 53) 2062.13955556
(4, 3) -1696.54911732
(4, 4) 4410.17902905
(4, 5) -1872.87344838
(4, 42) -130.515009576
: :
(91, 81) -1610.0550578
(91, 86) -199.343680224
(91, 87) -2330.41406097
(91, 90) -575.80373408
(91, 91) 7853.23899229
(92, 2) -357.41672785
(92, 8) 1735.59411191
(92, 50) -464.976034459
(92, 51) -1761.31189004
(92, 52) -3300.45367361
(92, 53) 1574.59387937
(92, 88) -250.325600254
(continues on next page)

1.5. Examples 67
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


(92, 89) 1334.11823335
(92, 92) 9219.18643706
(92, 93) -2607.52659081
(93, 2) 1510.24654193
(93, 8) -657.361661955
(93, 50) -1761.31189004
(93, 51) 54.1134516246
(93, 52) 1574.59387937
(93, 53) -315.793227627
(93, 88) 1334.11823335
(93, 89) -4348.13351285
(93, 92) -2607.52659081
(93, 93) 9821.16012014

In [13]: K.shape
Out[13]: (94, 94)

One would expect the shape of the global stiffness matrix 𝐾 to be (110, 110) i.e. to have the same number of rows and
columns as u. This matrix has been reduced by the fixed degrees of freedom imposed by the boundary conditions set
at the nodes on symmetry axes. To restore the matrix, temporarily remove the imposed boundary conditions:

In [14]: pb.remove_bcs()

Now we can calculate the force vector 𝑓 :

In [15]: f = pb.evaluator.eval_residual(u)

This leads to:

ValueError: shape mismatch: value array of shape (55,2) could not be broadcast to␣
Λ“β†’indexing result of shape (110,)

β€’ the original shape of the DOF vector needs to be restored:

In [16]: variables.vec.shape = (110,)

In [17]: f = pb.evaluator.eval_residual(u)

In [18]: f.shape
Out[18]: (110,)

In [19]: f
Out[19]:
array([ -4.73618436e+01, 1.42752386e+02, 1.56921124e-13, ...,
-2.06057393e-13, 2.13162821e-14, -2.84217094e-14])

Remember to restore the original boundary conditions previously removed in step [14]:

In [20]: pb.time_update()

To view the residual force vector, we can save it to a VTK file. This requires setting f to (a copy of) the variables as
follows:

68 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

In [21]: fvars = variables.copy()


In [22]: fvars.set_state(f, reduced=False)
In [23]: out = variables.create_output()
In [24]: pb.save_state('file.vtk', out=out)

Running the postproc.py script on file.vtk displays the average nodal forces as shown below:

The forces in the x- and y-directions at node 2 are:

In [25]: f.shape = (55, 2)


In [26]: f[2]
Out[26]: array([ 6.20373272e+02, -1.13686838e-13])

Great, we have an almost zero residual vertical load or force apparent at node 2 i.e. -1.13686838e-13 Newton. Let us
now check the stress at node 0, the centre of the specimen.

Generating output at element nodes

Previously we had calculated the stresses in the model but these were averaged from those calculated at Gauss quadrature
points within the elements. It is possible to provide custom integrals to allow the calculation of stresses with the Gauss
quadrature points at the element nodes. This will provide us a more accurate estimate of the stress at the centre of the
specimen located at node 0. The code below outlines one way to achieve this.

r"""
Diametrically point loaded 2-D disk with nodal stress calculation. See
:ref:`sec-primer`.

Find :math:`\ul{u}` such that:

.. math::
\int_{\Omega} D_{ijkl}\ e_{ij}(\ul{v}) e_{kl}(\ul{u})
= 0
\;, \quad \forall \ul{v} \;,

where

.. math::
D_{ijkl} = \mu (\delta_{ik} \delta_{jl}+\delta_{il} \delta_{jk}) +
(continues on next page)

1.5. Examples 69
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


\lambda \ \delta_{ij} \delta_{kl}
\;.
"""
from __future__ import print_function
from __future__ import absolute_import
from sfepy.examples.linear_elasticity.its2D_1 import *

from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson


from sfepy.discrete.fem.geometry_element import geometry_data
from sfepy.discrete import FieldVariable
from sfepy.discrete.fem import Field
import numpy as nm

gdata = geometry_data['2_3']
nc = len(gdata.coors)

def nodal_stress(out, pb, state, extend=False, integrals=None):


'''
Calculate stresses at nodal points.
'''

# Point load.
mat = pb.get_materials()['Load']
P = 2.0 * mat.get_data('special', 'val')[1]

# Calculate nodal stress.


pb.time_update()

if integrals is None: integrals = pb.get_integrals()

stress = pb.evaluate('ev_cauchy_stress.ivn.Omega(Asphalt.D, u)', mode='qp',


integrals=integrals, copy_materials=False)
sfield = Field.from_args('stress', nm.float64, (3,),
pb.domain.regions['Omega'])
svar = FieldVariable('sigma', 'parameter', sfield,
primary_var_name='(set-to-None)')
svar.set_from_qp(stress, integrals['ivn'])

print('\n==================================================================')
print('Given load = %.2f N' % -P)
print('\nAnalytical solution')
print('===================')
print('Horizontal tensile stress = %.5e MPa/mm' % (-2.*P/(nm.pi*150.)))
print('Vertical compressive stress = %.5e MPa/mm' % (-6.*P/(nm.pi*150.)))
print('\nFEM solution')
print('============')
print('Horizontal tensile stress = %.5e MPa/mm' % (svar()[0]))
print('Vertical compressive stress = %.5e MPa/mm' % (-svar()[1]))
print('==================================================================')
return out

asphalt = materials['Asphalt'][0]
(continues on next page)

70 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


asphalt.update({'D' : stiffness_from_youngpoisson(2, young, poisson)})
options.update({'post_process_hook' : 'nodal_stress',})

integrals = {
'ivn' : ('custom', gdata.coors, [gdata.volume / nc] * nc),
}

The output:

==================================================================
Given load = 2000.00 N

Analytical solution
===================
Horizontal tensile stress = 8.48826e+00 MPa/mm
Vertical compressive stress = 2.54648e+01 MPa/mm

FEM solution
============
Horizontal tensile stress = 7.57220e+00 MPa/mm
Vertical compressive stress = 2.58660e+01 MPa/mm
==================================================================

Not bad for such a coarse mesh! Re-running the problem using a finer mesh provides a more accurate solution:

==================================================================
Given load = 2000.00 N

Analytical solution
===================
Horizontal tensile stress = 8.48826e+00 MPa/mm
Vertical compressive stress = 2.54648e+01 MPa/mm

FEM solution
============
Horizontal tensile stress = 8.50042e+00 MPa/mm
Vertical compressive stress = 2.54300e+01 MPa/mm

To see how the FEM solution approaches the analytical one, try to play with the uniform mesh refinement level in the
Problem description file, namely lines 25, 26:

refinement_level = 0
filename_mesh = refine_mesh(filename_mesh, refinement_level)

The above computation could also be done in the IPython shell:

In [23]: from sfepy.applications import solve_pde


In [24]: from sfepy.discrete import (Field, FieldVariable, Material,
Integral, Integrals)
In [25]: from sfepy.discrete.fem.geometry_element import geometry_data

In [26]: gdata = geometry_data['2_3']


In [27]: nc = len(gdata.coors)
(continues on next page)

1.5. Examples 71
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


In [28]: ivn = Integral('ivn', order=-1,
....: coors=gdata.coors, weights=[gdata.volume / nc] * nc)

In [29]: pb, variables = solve_pde('sfepy/examples/linear_elasticity/its2D_2.py')

In [30]: stress = pb.evaluate('ev_cauchy_stress.ivn.Omega(Asphalt.D,u)',


....: mode='qp', integrals=Integrals([ivn]))
In [31]: sfield = Field.from_args('stress', nm.float64, (3,), pb.domain.regions['Omega'])
In [32]: svar = FieldVariable('sigma', 'parameter', sfield,
....: primary_var_name='(set-to-None)')
In [33]: svar.set_from_qp(stress, ivn)

In [34]: print('Horizontal tensile stress = %.5e MPa/mm' % (svar()[0]))


Horizontal tensile stress = 7.57220e+00 MPa/mm
In [35]: print('Vertical compressive stress = %.5e MPa/mm' % (-svar()[1]))
Vertical compressive stress = 2.58660e+01 MPa/mm

In [36]: mat = pb.get_materials()['Load']


In [37]: P = 2.0 * mat.get_data('special', 'val')[1]
In [38]: P
Out[38]: -2000.0

In [39]: print('Horizontal tensile stress = %.5e MPa/mm' % (-2.*P/(nm.pi*150.)))


Horizontal tensile stress = 8.48826e+00 MPa/mm
In [40]: print('Vertical compressive stress = %.5e MPa/mm' % (-6.*P/(nm.pi*150.)))
Vertical compressive stress = 2.54648e+01 MPa/mm

To wrap this tutorial up let’s explore SfePy’s probing functions.

Probing

As a bonus for sticking to the end of this tutorial see the following Problem description file that provides SfePy
functions to quickly and neatly probe the solution.

r"""
Diametrically point loaded 2-D disk with postprocessing and probes. See
:ref:`sec-primer`.

Find :math:`\ul{u}` such that:

.. math::
\int_{\Omega} D_{ijkl}\ e_{ij}(\ul{v}) e_{kl}(\ul{u})
= 0
\;, \quad \forall \ul{v} \;,

where

.. math::
D_{ijkl} = \mu (\delta_{ik} \delta_{jl}+\delta_{il} \delta_{jk}) +
\lambda \ \delta_{ij} \delta_{kl}
\;.
(continues on next page)

72 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


"""
from __future__ import absolute_import
from sfepy.examples.linear_elasticity.its2D_1 import *

from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson


from sfepy.postprocess.probes_vtk import Probe

import os
from six.moves import range

def stress_strain(out, pb, state, extend=False):


"""
Calculate and output strain and stress for given displacements.
"""
from sfepy.base.base import Struct
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm

ev = pb.evaluate
strain = ev('ev_cauchy_strain.2.Omega(u)', mode='el_avg')
stress = ev('ev_cauchy_stress.2.Omega(Asphalt.D, u)', mode='el_avg')

out['cauchy_strain'] = Struct(name='output_data', mode='cell',


data=strain, dofs=None)
out['cauchy_stress'] = Struct(name='output_data', mode='cell',
data=stress, dofs=None)

probe = Probe(out, pb.domain.mesh, probe_view=True)

ps0 = [[0.0, 0.0, 0.0], [ 0.0, 0.0, 0.0]]


ps1 = [[75.0, 0.0, 0.0], [ 0.0, 75.0, 0.0]]
n_point = 10

labels = ['%s -> %s' % (p0, p1) for p0, p1 in zip(ps0, ps1)]
probes = []
for ip in range(len(ps0)):
p0, p1 = ps0[ip], ps1[ip]
probes.append('line%d' % ip)
probe.add_line_probe('line%d' % ip, p0, p1, n_point)

for ip, label in zip(probes, labels):


fig = plt.figure()
plt.clf()
fig.subplots_adjust(hspace=0.4)
plt.subplot(311)
pars, vals = probe(ip, 'u')
for ic in range(vals.shape[1] - 1):
plt.plot(pars, vals[:,ic], label=r'$u_{%d}$' % (ic + 1),
lw=1, ls='-', marker='+', ms=3)
plt.ylabel('displacements')
plt.xlabel('probe %s' % label, fontsize=8)
plt.legend(loc='best', prop=fm.FontProperties(size=10))
(continues on next page)

1.5. Examples 73
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

sym_labels = ['11', '22', '12']

plt.subplot(312)
pars, vals = probe(ip, 'cauchy_strain')
for ii in range(vals.shape[1]):
plt.plot(pars, vals[:, ii], label=r'$e_{%s}$' % sym_labels[ii],
lw=1, ls='-', marker='+', ms=3)
plt.ylabel('Cauchy strain')
plt.xlabel('probe %s' % label, fontsize=8)
plt.legend(loc='best', prop=fm.FontProperties(size=8))

plt.subplot(313)
pars, vals = probe(ip, 'cauchy_stress')
for ii in range(vals.shape[1]):
plt.plot(pars, vals[:, ii], label=r'$\sigma_{%s}$' % sym_labels[ii],
lw=1, ls='-', marker='+', ms=3)
plt.ylabel('Cauchy stress')
plt.xlabel('probe %s' % label, fontsize=8)
plt.legend(loc='best', prop=fm.FontProperties(size=8))

opts = pb.conf.options
filename_results = os.path.join(opts.get('output_dir'),
'its2D_probe_%s.png' % ip)

fig.savefig(filename_results)

return out

materials['Asphalt'][0].update({'D' : stiffness_from_youngpoisson(2, young, poisson)})

options.update({
'post_process_hook' : 'stress_strain',
})

Probing applies interpolation to output the solution along specified paths. For the tutorial, line probing is done along
the x- and y-axes of the model.
Run SfePy to solve the problem and apply the probes:

./simple.py its2D_5.py

The probing function will generate the following figures that show the displacements, normal stresses and strains as
well as shear stresses and strains along the probe paths. Note that you need matplotlib installed to run this example.

74 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

The probing function also generates previews of the mesh with the probe paths.

1.5. Examples 75
SfePy Documentation, Release version: 2022.1+git.f9873484

Interactive Example

SfePy can be used also interactively by constructing directly the classes that corresponds to the keywords in the problem
description files. The following listing shows a script with the same (and more) functionality as the above examples:
#!/usr/bin/env python
"""
Diametrically point loaded 2-D disk, using commands for interactive use. See
:ref:`sec-primer`.

The script combines the functionality of all the ``its2D_?.py`` examples and
allows setting various simulation parameters, namely:

- material parameters
- displacement field approximation order
- uniform mesh refinement level

The example shows also how to probe the results as in


:ref:`linear_elasticity-its2D_4`, and how to display the results using Mayavi.
Using :mod:`sfepy.discrete.probes` allows correct probing of fields with the
approximation order greater than one.

In the SfePy top-level directory the following command can be used to get usage
information::

python sfepy/examples/linear_elasticity/its2D_interactive.py -h

Notes
-----

The ``--probe`` and ``--show`` options work simultaneously only if Mayavi and
Matplotlib use the same backend type (for example wx).
"""
from __future__ import absolute_import
import sys
from six.moves import range
sys.path.append('.')
from argparse import ArgumentParser, RawDescriptionHelpFormatter

import numpy as nm
(continues on next page)

76 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


import matplotlib.pyplot as plt

from sfepy.base.base import assert_, output, ordered_iteritems, IndexedStruct


from sfepy.discrete import (FieldVariable, Material, Integral, Integrals,
Equation, Equations, Problem)
from sfepy.discrete.fem import Mesh, FEDomain, Field
from sfepy.terms import Term
from sfepy.discrete.conditions import Conditions, EssentialBC
from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson
from sfepy.solvers.auto_fallback import AutoDirect
from sfepy.solvers.nls import Newton
from sfepy.discrete.fem.geometry_element import geometry_data
from sfepy.discrete.probes import LineProbe
from sfepy.discrete.projections import project_by_component

from sfepy.examples.linear_elasticity.its2D_2 import stress_strain


from sfepy.examples.linear_elasticity.its2D_3 import nodal_stress

def gen_lines(problem):
"""
Define two line probes.

Additional probes can be added by appending to `ps0` (start points) and


`ps1` (end points) lists.
"""
ps0 = [[0.0, 0.0], [0.0, 0.0]]
ps1 = [[75.0, 0.0], [0.0, 75.0]]

# Use enough points for higher order approximations.


n_point = 1000

labels = ['%s -> %s' % (p0, p1) for p0, p1 in zip(ps0, ps1)]
probes = []
for ip in range(len(ps0)):
p0, p1 = ps0[ip], ps1[ip]
probes.append(LineProbe(p0, p1, n_point))

return probes, labels

def probe_results(u, strain, stress, probe, label):


"""
Probe the results using the given probe and plot the probed values.
"""
results = {}

pars, vals = probe(u)


results['u'] = (pars, vals)
pars, vals = probe(strain)
results['cauchy_strain'] = (pars, vals)
pars, vals = probe(stress)
results['cauchy_stress'] = (pars, vals)

(continues on next page)

1.5. Examples 77
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


fig = plt.figure()
plt.clf()
fig.subplots_adjust(hspace=0.4)
plt.subplot(311)
pars, vals = results['u']
for ic in range(vals.shape[1]):
plt.plot(pars, vals[:,ic], label=r'$u_{%d}$' % (ic + 1),
lw=1, ls='-', marker='+', ms=3)
plt.ylabel('displacements')
plt.xlabel('probe %s' % label, fontsize=8)
plt.legend(loc='best', fontsize=10)

sym_indices = ['11', '22', '12']

plt.subplot(312)
pars, vals = results['cauchy_strain']
for ic in range(vals.shape[1]):
plt.plot(pars, vals[:,ic], label=r'$e_{%s}$' % sym_indices[ic],
lw=1, ls='-', marker='+', ms=3)
plt.ylabel('Cauchy strain')
plt.xlabel('probe %s' % label, fontsize=8)
plt.legend(loc='best', fontsize=10)

plt.subplot(313)
pars, vals = results['cauchy_stress']
for ic in range(vals.shape[1]):
plt.plot(pars, vals[:,ic], label=r'$\sigma_{%s}$' % sym_indices[ic],
lw=1, ls='-', marker='+', ms=3)
plt.ylabel('Cauchy stress')
plt.xlabel('probe %s' % label, fontsize=8)
plt.legend(loc='best', fontsize=10)

return fig, results

helps = {
'young' : "the Young's modulus [default: %(default)s]",
'poisson' : "the Poisson's ratio [default: %(default)s]",
'load' : "the vertical load value (negative means compression)"
" [default: %(default)s]",
'order' : 'displacement field approximation order [default: %(default)s]',
'refine' : 'uniform mesh refinement level [default: %(default)s]',
'probe' : 'probe the results',
'show' : 'show the results figure',
}

def main():
from sfepy import data_dir

parser = ArgumentParser(description=__doc__,
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('--version', action='version', version='%(prog)s')
parser.add_argument('--young', metavar='float', type=float,
(continues on next page)

78 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


action='store', dest='young',
default=2000.0, help=helps['young'])
parser.add_argument('--poisson', metavar='float', type=float,
action='store', dest='poisson',
default=0.4, help=helps['poisson'])
parser.add_argument('--load', metavar='float', type=float,
action='store', dest='load',
default=-1000.0, help=helps['load'])
parser.add_argument('--order', metavar='int', type=int,
action='store', dest='order',
default=1, help=helps['order'])
parser.add_argument('-r', '--refine', metavar='int', type=int,
action='store', dest='refine',
default=0, help=helps['refine'])
parser.add_argument('-s', '--show',
action="store_true", dest='show',
default=False, help=helps['show'])
parser.add_argument('-p', '--probe',
action="store_true", dest='probe',
default=False, help=helps['probe'])
options = parser.parse_args()

assert_((0.0 < options.poisson < 0.5),


"Poisson's ratio must be in ]0, 0.5[!")
assert_((0 < options.order),
'displacement approximation order must be at least 1!')

output('using values:')
output(" Young's modulus:", options.young)
output(" Poisson's ratio:", options.poisson)
output(' vertical load:', options.load)
output('uniform mesh refinement level:', options.refine)

# Build the problem definition.


mesh = Mesh.from_file(data_dir + '/meshes/2d/its2D.mesh')
domain = FEDomain('domain', mesh)

if options.refine > 0:
for ii in range(options.refine):
output('refine %d...' % ii)
domain = domain.refine()
output('... %d nodes %d elements'
% (domain.shape.n_nod, domain.shape.n_el))

omega = domain.create_region('Omega', 'all')


left = domain.create_region('Left',
'vertices in x < 0.001', 'facet')
bottom = domain.create_region('Bottom',
'vertices in y < 0.001', 'facet')
top = domain.create_region('Top', 'vertex 2', 'vertex')

field = Field.from_args('fu', nm.float64, 'vector', omega,


(continues on next page)

1.5. Examples 79
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


approx_order=options.order)

u = FieldVariable('u', 'unknown', field)


v = FieldVariable('v', 'test', field, primary_var_name='u')

D = stiffness_from_youngpoisson(2, options.young, options.poisson)

asphalt = Material('Asphalt', D=D)


load = Material('Load', values={'.val' : [0.0, options.load]})

integral = Integral('i', order=2*options.order)


integral0 = Integral('i', order=0)

t1 = Term.new('dw_lin_elastic(Asphalt.D, v, u)',
integral, omega, Asphalt=asphalt, v=v, u=u)
t2 = Term.new('dw_point_load(Load.val, v)',
integral0, top, Load=load, v=v)
eq = Equation('balance', t1 - t2)
eqs = Equations([eq])

xsym = EssentialBC('XSym', bottom, {'u.1' : 0.0})


ysym = EssentialBC('YSym', left, {'u.0' : 0.0})

ls = AutoDirect({})

nls_status = IndexedStruct()
nls = Newton({}, lin_solver=ls, status=nls_status)

pb = Problem('elasticity', equations=eqs)

pb.set_bcs(ebcs=Conditions([xsym, ysym]))

pb.set_solver(nls)

# Solve the problem.


variables = pb.solve()
output(nls_status)

# Postprocess the solution.


out = variables.create_output()
out = stress_strain(out, pb, variables, extend=True)
pb.save_state('its2D_interactive.vtk', out=out)

gdata = geometry_data['2_3']
nc = len(gdata.coors)

integral_vn = Integral('ivn', coors=gdata.coors,


weights=[gdata.volume / nc] * nc)

nodal_stress(out, pb, variables, integrals=Integrals([integral_vn]))

if options.probe:
(continues on next page)

80 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


# Probe the solution.
probes, labels = gen_lines(pb)

sfield = Field.from_args('sym_tensor', nm.float64, 3, omega,


approx_order=options.order - 1)
stress = FieldVariable('stress', 'parameter', sfield,
primary_var_name='(set-to-None)')
strain = FieldVariable('strain', 'parameter', sfield,
primary_var_name='(set-to-None)')

cfield = Field.from_args('component', nm.float64, 1, omega,


approx_order=options.order - 1)
component = FieldVariable('component', 'parameter', cfield,
primary_var_name='(set-to-None)')

ev = pb.evaluate
order = 2 * (options.order - 1)
strain_qp = ev('ev_cauchy_strain.%d.Omega(u)' % order, mode='qp')
stress_qp = ev('ev_cauchy_stress.%d.Omega(Asphalt.D, u)' % order,
mode='qp', copy_materials=False)

project_by_component(strain, strain_qp, component, order)


project_by_component(stress, stress_qp, component, order)

all_results = []
for ii, probe in enumerate(probes):
fig, results = probe_results(u, strain, stress, probe, labels[ii])

fig.savefig('its2D_interactive_probe_%d.png' % ii)
all_results.append(results)

for ii, results in enumerate(all_results):


output('probe %d:' % ii)
output.level += 2
for key, res in ordered_iteritems(results):
output(key + ':')
val = res[1]
output(' min: %+.2e, mean: %+.2e, max: %+.2e'
% (val.min(), val.mean(), val.max()))
output.level -= 2

if options.show:
# Show the solution. If the approximation order is greater than 1, the
# extra DOFs are simply thrown away.
from sfepy.postprocess.viewer import Viewer

view = Viewer('its2D_interactive.vtk')
view(vector_mode='warp_norm', rel_scaling=1,
is_scalar_bar=True, is_wireframe=True)

if __name__ == '__main__':
main()

1.5. Examples 81
SfePy Documentation, Release version: 2022.1+git.f9873484

The script can be run from the SfePy top-level directory, assuming the in-place build, as follows:

python sfepy/examples/linear_elasticity/its2D_interactive.py

The script allows setting several parameters that influence the solution, see:

python sfepy/examples/linear_elasticity/its2D_interactive.py -h

for the complete list. Besides the material parameters, a uniform mesh refinement level and the displacement field
approximation order can be specified. The script demonstrates how to
β€’ project a derived quantity, that is evaluated in quadrature points (e.g. a strain or stress), into a field variable;
β€’ probe the solution defined in the field variables.
Using sfepy.discrete.probes allows correct probing of fields with the approximation order greater than one.
The end.

1.5.2 Using Salome with SfePy

Introduction

Salome is a powerful open-source tool for generating meshes for numerical simulation and post processing the results.
This is a short tutorial on using Salome as a preprocessor for preparing meshes for use with SfePy.

Tutorial prerequisites

This tutorial assumes that you have a working copy of Salome. It is possible to build Salome from source code.
Fortunately, for the less brave, many pre-compiled binaries for different platforms are available at the Salome download
page. Registration for a free account may be required to download from the preceding site.
In addition, this tutorial assumes you have a working copy of SfePy with MED read support. See the Installation for
help. Note that it is not actually necessary to β€œinstall” SfePy; one may run the code from the source directory (see
notation below) after compilation of the C extension modules (again, see the installation notes if you are confused).

Note on notation used in this tutorial

We are using the following notations:


β€’ <sfepy_root>: the root directory of the SfePy source code
β€’ <work_dir>: the working directory where you plan to save your files

Step 1: Using Salome

Salome has its own set of tutorials and community resources. It is suggested you look around on Salome web site to
familiarize yourself with the available resources.
This tutorial follows the EDF Exercise 1 available from the Salome Tutorial Site. Go ahead and complete this tutorial
now. We will use the result from there in the following.
This is the mesh you should end up with:

82 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

Step 2: Exporting mesh from Salome

In the Salome MESH module, right click on the mesh object Mesh_Partition_Hexa you created in the Salome EDF
Exercise 1 Tutorial and click Export to MED file. Save the file as Mesh_Partition_Hexa.med in your working
directory <work_dir>.

Step 3: Copy SfePy project description files

In this tutorial, we will assume that we need to solve a linear elasticity problem on the mesh generated by Salome. Since
the Salome mesh looks a bit like a fish, we will try to simulate the fish waving its tail.
Copy the file <sfepy_root>/sfepy/examples/linear_elasticity/linear_elastic.py to <work_dir>. Use
your favorite python editor to load this file. We will customize this file for our purposes.

Step 4: Modify linear_elastic.py

Mesh specification

The first thing we have to do is tell SfePy to use our new mesh. Change the line

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

to

filename_mesh = 'Mesh_Partition_Hexa.med'

1.5. Examples 83
SfePy Documentation, Release version: 2022.1+git.f9873484

Region specification

Next, we have to define sensible Regions for the mesh. We will apply a displacement to the Tail and keep the Top and
Bottom of the fish fixed. Change the lines

regions = {
'Omega' : 'all',
'Left' : ('vertices in (x < 0.001)', 'facet'),
'Right' : ('vertices in (x > 0.099)', 'facet'),
'SomewhereTop' : ('vertices in (z > 0.017) & (x > 0.03) & (x < 0.07)', 'vertex'),
}

to

regions = {
'Omega' : 'all',
'Tail' : ('vertices in (x < -94)', 'facet'),
'TopFixed' : ('vertices in (z > 9.999) & (x > 54)', 'facet'),
'BotFixed' : ('vertices in (z < 0.001) & (x > 54)', 'facet'),
}

Field specification

The Salome mesh uses hexahedral linear order elements; in SfePy notation these are called 3_8, see User’s Guide.
Just keep the lines

fields = {
'displacement': ('real', 'vector', 'Omega', 1),
}

Boundary condition specifications

In this section, we tell SfePy to fix the top and bottom parts of the β€œhead” of the fish and move the tail 10 units to the
side (z direction).
Change the lines

ebcs = {
'Fixed' : ('Left', {'u.all' : 0.0}),
'Displaced' : ('Right', {'u.0' : 0.01, 'u.[1,2]' : 0.0}),
'PerturbedSurface' : ('SomewhereTop', {'u.2' : 0.005}),
}

to

ebcs = {
'TopFixed' : ('TopFixed', {'u.all' : 0.0}),
'BotFixed' : ('BotFixed', {'u.all' : 0.0}),
'Displaced' : ('Tail', {'u.2' : 10, 'u.[0,1]' : 0.0}),
}

84 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

Step 5: Run SfePy

Save your changes to linear_elastic.py. Now it’s time to run the SfePy calculation. In your <work_dir> in your
terminal type:

./simple.py linear_elastic.py

This will run the SfePy calculation. Some progress information is printed to your screen and the residual (a measure
of the convergence of the solution) is printed for each iteration of the solver. The solver terminates when this residual
is less than a certain value. It should only take 1 iteration since we are solving a linear problem. The results will be
saved to Mesh_Partition_Hexa.vtk.
Now we can view the results of our work. In your terminal, type:

./postproc.py --wireframe --vector-mode=warp_norm -s 2 Mesh_Partition_Hexa.vtk

You should get the following plot. The undeformed mesh is displayed with a wireframe for comparison. Notice how
the fish is bending its tail in response to the applied displacement.
Now you should be able to use meshes created in Salome with SfePy!

1.5.3 Preprocessing: FreeCAD/OpenSCAD + Gmsh

Introduction

There are several open source tools for preparing 2D and 3D finite element meshes like Salome, FreeCAD, Gmsh,
Netgen, etc. Most of them are GUI based geometrical modeling and meshing environments/tools but they also usually
allow using their libraries in user scripts. Some of the above mentioned tools are handy for solid modeling, some
of them are great for meshing. This tutorial shows how to combine solid geometry modeling functions provided by
FreeCAD or OpenSCAD with meshing functions of Gmsh.
The collaboration of modeling, meshing and conversion tools and the workflow are illustrated in the following scheme.

1.5. Examples 85
SfePy Documentation, Release version: 2022.1+git.f9873484

Creating geometry using FreeCAD

Functionalities of FreeCAD are accessible to Python and can be used to define geometrical models in simple Python
scripts. There is a tutorial related to Python scripting in FreeCAD.
The first step in creating a Python script is to set up a path to the FreeCAD libraries and import all required modules:

1 import sys
2 FREECADPATH = '/usr/lib/freecad/lib/'
3 sys.path.append(FREECADPATH)
4

5 from FreeCAD import Base, newDocument


6 import Part
7 import Draft
8 import ProfileLib.RegularPolygon as Poly

Now, a new empty FreeCAD document can be defined as:

doc = newDocument()

All new objects describing the geometry will be added to this document.
In the following lines a geometrical model of a screwdriver handle will be created. Let’s start by defining a sphere and
a cylinder and join these objects into the one called uni:

1 radius = 0.01
2 height = 0.1
3

4 cyl = doc.addObject("Part::Cylinder", "cyl")


5 cyl.Radius = radius
6 cyl.Height = height
7

8 sph = doc.addObject("Part::Sphere", "sph")


9 sph.Radius = radius
10

11 uni = doc.addObject("Part::MultiFuse", "uni")


12 uni.Shapes = [cyl, sph]

Create a polygon, revolve it around the z-axis to create a solid and use the result as the cutting tool applied to uni
object:

1 ske = doc.addObject('Sketcher::SketchObject', 'Sketch')


2 ske.Placement = Base.Placement(Base.Vector(0, 0, 0),
3 Base.Rotation(-0.707107, 0, 0, -0.707107))
4 Poly.makeRegularPolygon('Sketch', 5,
5 Base.Vector(-1.2 * radius, 0.9 * height, 0),
6 Base.Vector(-0.8 * radius, 0.9 * height, 0))
7

8 cut = doc.addObject("PartDesign::Revolution", "Revolution")


9 cut.Sketch = ske
10 cut.ReferenceAxis = (ske, ['V_Axis'])
11 cut.Angle = 360.0
12

13 dif = doc.addObject("Part::Cut", "dif")


(continues on next page)

86 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


14 dif.Base = uni
15 dif.Tool = cut

Create a cylinder, make a polar array of the cylinder objects and subtract it from the previous result:

1 cyl1 = doc.addObject("Part::Cylinder", "cyl1")


2 cyl1.Radius = 0.2 * radius
3 cyl1.Height = 1.1 * height
4 cyl1.Placement = Base.Placement(Base.Vector(-1.1 * radius, 0, -0.2 * height),
5 Base.Rotation(0, 0, 0, 1))
6

7 arr = Draft.makeArray(cyl1, Base.Vector(1, 0, 0), Base.Vector(0, 1, 0), 2, 2)


8 arr.ArrayType = "polar"
9 arr.NumberPolar = 6
10

11 dif2 = doc.addObject("Part::Cut", "dif2")


12 dif2.Base = dif
13 dif2.Tool = arr

Create a middle hole for the screwdriver metal part:

1 cyl2 = doc.addObject("Part::Cylinder", "cyl2")


2 cyl2.Radius = 0.3 * radius
3 cyl2.Height = height
4

5 dif3 = doc.addObject("Part::Cut", "dif3")


6 dif3.Base = dif2
7 dif3.Tool = cyl2

Finally, recompute the geometry, export the part to the STEP file and save the document in FreeCAD format (not really
needed for subsequent mesh generation, but may be useful for visualization and geometry check):

1 doc.recompute()
2

3 Part.export([dif3], 'screwdriver_handle.step')
4

5 doc.saveAs('screwdriver_handle.FCStd')

A finite element mesh can be generated directly in FreeCAD using MeshPart module:

1 import MeshPart
2

3 mesh = doc.addObject("Mesh::Feature", "Mesh")


4 mesh.Mesh = MeshPart.meshFromShape(Shape=dif3.Shape, MaxLength=0.002)
5 mesh.Mesh.write("./screwdriver_handle.bdf", "NAS", "mesh")

The meshing function of MeshPart module is limited to triangular grids so it is better to use Gmsh mesh generator
which can provide triangular and quadrilateral meshes in 2D or tetrahedral and hexahedral meshes in 3D. Gmsh allows
to control the meshing process through a wide range of parameters. Meshing by Gmsh will be described in section
Gmsh - generating finite element mesh.

1.5. Examples 87
SfePy Documentation, Release version: 2022.1+git.f9873484

The example of screwdriver handle: screwdriver_handle.py.


There are two simple ways how to discover Python calls of FreeCAD functions. You can enable β€œshow script
commands in python console” in Edit->Preferences->General->Macro and the Python console by selecting
View->Views->Python Console and all subsequent operations will be printed in the console as the Python code.
The second way is to switch on the macro recording function (Macro->Macro recording ...) which generates a
Python script (FCMacro file) containing all the code related to actions in the FreeCAD graphical interface.

Creating geometry using OpenSCAD

The alternative tool for solid geometrical modeling is OpenSCAD - β€œThe Programmers Solid 3D CAD Modeller”.
It has its own description language based on functional programming that is used to construct solid models using
geometrical primitives similar to FreeCAD. Solid geometries can be exported to several file formats including STL and
CSG. OpenSCAD allows solid modeling based on Constructive Solid Geometry (CSG) principles and extrusion of 2D
objects into 3D. The model of a screwdriver handle presented in the previous section can be defined in OpenSCAD by
the following code (screwdriver_handle.scad):

1 radius = 0.01;
2 height = 0.1;
3 $fn = 50;
4

5 difference() {
6 difference() {
7 difference() {
8 union() {
9 cylinder(center=false, h=height, r=radius);
10 sphere(radius);
(continues on next page)

88 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


11 };
12 translate([0, 0, 0.9*height])
13 rotate_extrude()
14 polygon([[0.8*radius, 0], [1.8*radius, -0.577*radius], [1.8*radius, 0.
Λ“β†’577*radius]]);

15 }
16 cylinder(center=false, h=1.1*height, r=0.3*radius);
17 }
18 for (i = [1:6]) {
19 rotate([0, 0, 360/6*i])
20 translate([-1.1*radius, 0.0, -0.2*height])
21 cylinder(center=false, h=1.1*height, r=0.2*radius);
22 }
23 }

To generate a finite element mesh of the solid geometry the model must be exported to a suitable file format. OpenSCAD
has limited export options, but by using FreeCAD import/export functions, it is possible to find a workaround. The
OpenSCAD model can be exported to the CSG file format and FreeCAD can be used as a mesh converter to the STEP
format:

1 import sys
2 sys.path.append('/usr/lib/freecad/lib/')
3 sys.path.append('/usr/lib/freecad/Mod/OpenSCAD/')
4

5 import FreeCAD
6 import Part
7 import importCSG
8

9 importCSG.open('screwdriver_handle.csg')
10 Part.export([FreeCAD.ActiveDocument.Objects[-1]], 'screwdriver_handle.step')

1.5. Examples 89
SfePy Documentation, Release version: 2022.1+git.f9873484

Gmsh - generating finite element mesh

Gmsh can create finite element meshes using geometrical models imported from STEP, IGES and BRep files (has to
be compiled with OpenCASCADE support).
The following GEO file imports screwdriver_handle.step file and defines a field controlling the mesh size
(screwdriver_handle.geo):

1 Merge "screwdriver_handle.step";
2

3 Field[1] = MathEval;
4 Field[1].F = "0.002";
5 Background Field = 1;

Now, run Gmsh generator and export the mesh into the MSH format in which all surface and volumetric elements are
stored:

gmsh -3 -format msh -o screwdriver_handle.msh screwdriver_handle.geo

By converting the MSH file into the VTK format using script/convert_mesh.py:

script/convert_mesh.py -d 3 screwdriver_handle.msh screwdriver_handle.vtk

the surface elements are discarded and only the volumetric mesh is preserved.

Note: planar 2D meshes

To create a planar 2D mesh, such as

90 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

that can be described by this Gmsh code, the mesh generator can be called as follows:

gmsh -2 -format msh -o circle_in_square.msh circle_in_square.geo

This, however is not enough to create a truly 2D mesh - the created mesh vertices still have the third, 𝑧, component
which is equal to zero. In order to remove the third component, use:

script/convert_mesh.py --2d circle_in_square.msh circle_in_square.h5

Now, in the resulting circle_in_square.h5, each vertex has only two coordinates. Another way of generating the
2D mesh is to use the legacy VTK format as follows:

gmsh -2 -format vtk -o circle_in_square.vtk circle_in_square.geo


script/convert_mesh.py circle_in_square.vtk circle_in_square.h5

This is due to the fact that the legacy VTK does not support 2D vertices and so the VTKMeshIO reader tries to detect
the planar geometry by comparing the 𝑧 components to zero - the --2d option of script/convert_mesh.py is not
needed in this case.

Multipart models

Meshing models composed of parts with different material groups is a little bit tricky task. But there are some more or
less general ways of doing that. Here, the method using functions of Gmsh for periodic meshes will be shown.
The screwdriver handle example is extended by adding a screwdriver shank. The new part is composed of a cylinder
trimmed at one end:

1 cyl3 = doc.addObject("Part::Cylinder", "cyl3")


2 cyl3.Radius = 0.3 * radius
3 cyl3.Height = height
4 cyl3.Placement = Base.Placement(Base.Vector(0, 0, height),
5 Base.Rotation(0, 0, 0, 1))
6

7 tip1 = doc.addObject("Part::Box", "tip1")


8 tip1.Length = radius
9 tip1.Width = 2 * radius
10 tip1.Height = 3 * radius
11 tip1.Placement = Base.Placement(Base.Vector(0, -radius, 1.71 * height),
12 Base.Rotation(Base.Vector(0, 1, 0), -10),
13 Base.Vector(0, 0, 3 * radius))
14

15 tip2 = doc.addObject("Part::Mirroring", "tip2")


16 tip2.Source = tip1
17 tip2.Normal = (1, 0, 0)
18

19 tip3 = doc.addObject("Part::MultiFuse", "tip3")


20 tip3.Shapes = [tip1, tip2]
21

22 dif4 = doc.addObject("Part::Cut", "dif4")


23 dif4.Base = cyl3
24 dif4.Tool = tip3
25

26 uni2 = doc.addObject("Part::MultiFuse", "uni2")


27 uni2.Shapes = [cyl2, dif4]

1.5. Examples 91
SfePy Documentation, Release version: 2022.1+git.f9873484

The handle and shank are exported to the STEP file as two separated parts:

1 doc.recompute()
2

3 Part.export([dif3, uni2], 'screwdriver_full.step')


4 doc.saveAs('screwdriver_full.FCStd')

The full screwdriver example (handle + shank): screwdriver_full.py.


To create a coincidence mesh on the handle and shank interface, it is necessary to identify the interface surfaces and
declare them to be periodic in the GEO file. The identification has to be done manually in the Gmsh graphical interface.

The input file for Gmsh is than as follows (screwdriver_full.geo):

1 Merge "screwdriver_full.step";
2

3 Periodic Surface 5 {7} = 26 {67};


4 Periodic Surface 3 {6, 2, -6, 7} = 27 {68, 69, -68, 67};
5

6 Physical Volume(1) = {1};


7 Physical Volume(2) = {2};
(continues on next page)

92 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


8

9 Field[1] = MathEval;
10 Field[1].F = "0.0015";
11 Background Field = 1;

where the first pair of periodic surfaces corresponds to the common circle faces (bottom of the shank) and the second
pair to the common cylindrical surfaces. See Gmsh Reference manual for details on periodic meshing.
Using the above stated GEO file, Gmsh creates a mesh containing duplicate vertices on the handle/shank interface.
These duplicate vertices can be removed during the conversion to the VTK format by giving --merge (or just -m)
argument to convert_mesh.py script:

script/convert_mesh.py -m screwdriver_full.msh screwdriver_full.vtk

In order to extract the cells by the physical groups use the conversion script with --save-per-mat argument:

script/convert_mesh.py --save-per-mat screwdriver_full.vtk screwdriver.vtk

It produces screwdriver.vtk contaning the original mesh and screwdriver_matid_1.vtk, screwdriver_matid_2.vtk files
containing only the cells of a given physical group and all vertices of the original mesh.

When using OpenSCAD, define the full screwdriver geometry as (screwdriver_full.scad):

1 radius = 0.01;
2 height = 0.1;
3 $fn = 50;
4

5 module tip() {
6 rotate([0, -10, 0])
7 translate([0, -radius, -3*radius])
8 cube([radius, 2*radius, 3*radius], center=false);
9 }
10

11 difference() {
12 difference() {
13 difference() {
(continues on next page)

1.5. Examples 93
SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


14 union() {
15 cylinder(center=false, h=height, r=radius);
16 sphere(radius);
17 };
18 translate([0, 0, 0.9*height])
19 rotate_extrude()
20 polygon([[0.8*radius, 0], [1.8*radius, -0.577*radius], [1.8*radius, 0.
Λ“β†’577*radius]]);

21 }
22 cylinder(center=false, h=height, r=0.3*radius);
23 }
24 for (i = [1:6]) {
25 rotate([0, 0, 360/6*i])
26 translate([-1.1*radius, 0.0, -0.2*height])
27 cylinder(center=false, h=1.1*height, r=0.2*radius);
28 }
29 }
30

31 union() {
32 difference() {
33 translate([0, 0, height])
34 cylinder(center=false, h=height, r=0.3*radius);
35 translate([0, 0, 1.71*height + 3*radius])
36 union() {
37 tip();
38 mirror ([1, 0, 0]) tip();
39 }
40 }
41 cylinder(center=false, h=height, r=0.3*radius);
42 }

and convert the CSG file to the STEP file by:

1 importCSG.open('screwdriver_full.csg')
2 top_group = FreeCAD.ActiveDocument.Objects[-1]
3 Part.export(top_group.OutList, 'screwdriver_full.step')

Since the different tools for geometry definition have been used, the numbering of geometric objects may differ and the
surface and edge numbers have to be changed in the GEO file:

Periodic Surface 5 {6} = 26 {66};


Periodic Surface 3 {5, 2, -5, 6} = 27 {67, 68, -67, 66};

Note: The numbering of objects may vary between FreeCAD, OpenSCAD and Gmsh versions.

94 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

1.5.4 Material Identification

Introduction

This tutorial shows identification of material parameters of a composite structure using data (force-displacement curves)
obtained by a standard tensile test.

Composite structure

The unidirectional long fiber carbon-epoxy composite is considered. Its microstructure was analysed by the scanning
electron microscopy and the data, volume fractions and fibers cross-sections, were used to generate a periodic finite
element mesh (representative volume element - RVE) representing the random composite structure at the microscopic
level (the random structure generation algorithm is described in1 ):

This RVE is used in the micromechanical FE analysis which is based on the two-scale homogenization method.

Material testing

Several carbon-expoxy specimens with different fiber orientations (0, 30, 60 and 90 degrees) were subjected to the
tensile test in order to obtain force-elongation dependencies, see2 . The slopes of the linearized dependencies were used
in an objective function of the identification process.
1 Lubachevsky B. D., How to Simulate Billiards and Similar Systems, Journal of Computational Physics, 94(2), 1991. http://arxiv.org/PS_cache/

cond-mat/pdf/0503/0503627v2.pdf
2 SrbovÑ H., Kroupa T., Zemčík R., Identification of the Material Parameters of a Unidirectional Fiber Composite Using a Micromodel, Materiali

in Tehnologije, 46(5), 2012, 431-434.

1.5. Examples 95
SfePy Documentation, Release version: 2022.1+git.f9873484

Numerical simulation

The linear isotropic material model is used for both components (fiber and matrix) of the composite so only four material
parameters (Young’s modulus and Poisson’s ratio for each component) are necessary to fully describe the mechanical
behavior of the structure.
The numerical simulations of the tensile tests are based on the homogenization method applied to the linear elastic prob-
lem3 . The homogenization procedure results in the microscopic problem solved within the RVE and the macroscopic
problem that involves the homogenized elastic coefficients.

3 Pinho-da-Cruz L., Oliveira J. A. and Teixeira-Dias F., Asymptotic homogenization in linear elasticity. Part I: Mathematical formulation and

finite element modeling, Computational Materials Science, 45(4), 2009, 1073–1080.

96 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

Homogenized coefficients

The problem at the microscopic level is formulated in terms of characteristic response functions and its solution is used
to evaluate the homogenized elasticity tensor. The microscopic problem has to be solved with the periodic boundary
conditions.
The following SfePy description file is used for definition of the microscopic problem: homogenization_opt_src.
In the case of the identification process function get_mat() obtains the material parameters (Young’s modules, Poisson’s
ratios) from the outer identification loop. Otherwise these parameters are given by values.
Notice the use of parametric_hook (Miscellaneous) to pass around the optimization parameters.

Macroscopic simulation

The homogenized elasticity problem is solved for the unknown macroscopic displacements and the elongation of the
composite specimen is evaluated for a given loading. These values are used to determine the slopes of the calculated
force-elongation dependencies which are required by the objective function.
The SfePy description file for the macroscopic analysis: linear_elasticity_opt_src.

Identification procedure

The identification of material parameters, i.e. the Young’s modulus and Poisson’s ratio, of the epoxy matrix (πΈπ‘š , πœˆπ‘š )
and carbon fibers (𝐸𝑓 , πœˆπ‘“ ) can be formulated as a minimization of the following objective function:
(οΈƒ )οΈƒ2
𝑖
βˆ‘οΈ π‘˜π‘π‘œπ‘šπ‘ (x)
Ξ¦(x) = 1βˆ’ 𝑖
, (1.9)
π‘˜π‘’π‘₯𝑝
π‘–βˆˆ{0,30,60,90}

where π‘˜π‘π‘œπ‘šπ‘
𝑖
and π‘˜π‘’π‘₯𝑝
𝑖
are the computed and measured slopes of the force-elongation tangent lines for a given fiber ori-
entation. This function is minimized using scipy.optimize.fmin_tnc(), considering bounds of the identified parameters.
Tho following steps are performed in each iteration of the optimization loop:
1. Solution of the microscopic problem, evaluation of the homogenized elasticity tensor.
2. Solution of the macroscopic problems for different fiber orientations (0, 30, 60, 90), this is incorporated by
appropriate rotation of the elasticity tensor.
3. Evaluation of the objective function.
Python script for material identification: material_opt_src.

Running identification script

Run the script from the command shell as (from the top-level directory of SfePy):

$ python sfepy/examples/homogenization/material_opt.py

The iteration process is monitored using graphs where the values of the objective function and material parameters are
plotted.

1.5. Examples 97
SfePy Documentation, Release version: 2022.1+git.f9873484

The resulting values of 𝐸𝑓 , πœˆπ‘“ , πΈπ‘š , πœˆπ‘š can be found at the end of the script output:

>>> material optimization FINISHED <<<


material_opt_micro: terminated
optimized parameters: [1.71129526e+11 3.20844131e-01 2.33507829e+09 2.00000000e-01]

So that:
𝐸𝑓 = 171.13 GPa
πœˆπ‘“ = 3.21
πΈπ‘š = 2.34 GPa
πœˆπ‘š = 0.20
Note: The results may vary across SciPy versions and related libraries.

1.5.5 Mesh parametrization

Introduction

When dealing with shape optimization we usually need to modify a FE mesh using a few optimization parameters
describing the mesh geometry. The B-spline parametrization offers an efficient way to do that. A mesh region (2D or
3D) that is to be parametrized is enclosed in the so called spline-box and the positions of all vertices inside the box can
be changed by moving the control points of the B-spline curves.
There are two different classes for the B-spline parametrization implemented in SfePy (module sfepy.mesh.
splinebox): SplineBox and SplineRegion2D. The first one defines a rectangular parametrization box in 2D or
3D while the second one allows to set up an arbitrary shaped region of parametrization in 2D.

98 Chapter 1. Documentation
SfePy Documentation, Release version: 2022.1+git.f9873484

SplineBox

The rectangular B-spline parametrization is created as follows:

1 from sfepy.mesh.splinebox import SplineBox


2

3 spb = SplineBox(<bbox>, <coors>, <nsg>)

the first parameter defines the range of the box in each dimension, the second parameter is the array of coordinates
(vertices) to be parametrized and the last one (optional) determines the number of control points in each dimension.
The number of the control points (𝑛𝑐𝑝) is calculated as:

𝑛𝑐𝑝𝑖 = 𝑛𝑠𝑔𝑖 + π‘‘π‘’π‘”π‘Ÿπ‘’π‘’, 𝑖 = 1, 2(, 3) (1.10)

where π‘‘π‘’π‘”π‘Ÿπ‘’π‘’ is the degree of the B-spline curve (default value: 3 = cubic spline) and 𝑛𝑠𝑔 is the number of the spline
segments (default value: [1,1(,1)] = 4 control points for all dimensions).
The position of the vertices can be modified by moving the control points:

spb.move_control_point(<cpoint>, <val>)

where <cpoint> is the index or position of the control point, for explanation see the following figure.

The displacement is given by <val>. The modified coordinates of the vertices are evaluated by:

new_coors = spb.evaluate()

Example

β€’ Create a new 2D SplineBox with the left bottom corner at [-1,-1] and the right top corner at [1, 0.6] which has
5 control points in x-direction and 4 control points in y-direction:

1 from sfepy.mesh.splinebox import SplineBox


2 from sfepy.discrete.fem import Mesh
3

4 mesh = Mesh.from_file('meshes/2d/square_tri1.mesh')
5 spb = SplineBox([[-1, 1], [-1, 0.6]], mesh.coors, nsg=[2,1])

β€’ Modify the position of mesh coordinates by moving three control points (with indices 1,2 and 3):

1.5. Examples 99
SfePy Documentation, Release version: 2022.1+git.f9873484

1 spb.move_control_point(1, [0.1, -0.2])


2 spb.move_control_point(2, [0.2, -0.3])
3 spb.move_control_point(3, [0.0, -0.1])

β€’ Evaluate the new coordinates:

mesh.cmesh.coors[:] = spb.evaluate()

β€’ Write the deformed mesh and the spline control net (the net of control points) into vtk files:

spb.write_control_net('square_tri1_spbox.vtk')
mesh.write('square_tri1_deform.vtk')

The following figures show the undeformed (left) and deformed (right) mesh and the control net.

SplineRegion2D

In this case, the region (only in 2D) of parametrization is defined by four B-spline curves:

1 from sfepy.mesh.splinebox import SplineRegion2D


2

3 spb = SplineRegion2D([<bspl1>, <bspl2>, <bspl3>, <bspl4>], <coors>)

The curves must form a closed loop, must be oriented counterclockwise and the opposite curves (<bspl1>, <bspl3>
and <bspl2>, <bspl4>) must have the same number of control points and the same knot vectors, see the figure below,
on the left.

100 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

The position of the selected vertices, depicted in the figure on the right, are driven by the control points in the same
way as explained above for SplineBox.
Note: Initializing SplineRegion2D may be time consuming due to the fact that for all vertex coordinates the spline
parameters have to be found using an optimization method in which the B-spline basis is repeatedly evaluated.

Example

β€’ First of all, define four B-spline curves (the default degree of the spline curve is 3) representing the boundary of
a parametrization area:

1 from sfepy.mesh.bspline import BSpline


2

3 # left / right boundary


4 line_l = nm.array([[-1, 1], [-1, .5], [-1, 0], [-1, -.5]])
5 line_r = nm.array([[0, -.2], [.1, .2], [.3, .6], [.4, 1]])
6

7 sp_l = BSpline()
8 sp_l.approximate(line_l, ncp=4)
9 kn_lr = sp_l.get_knot_vector()
10

11 sp_r = BSpline()
12 sp_r.approximate(line_r, knots=kn_lr)
13

14 # bottom / top boundary


15 line_b = nm.array([[-1, -.5], [-.8, -.6], [-.5, -.4], [-.2, -.2], [0, -.2]])
16 line_t = nm.array([[.4, 1], [0, 1], [-.2, 1], [-.6, 1], [-1, 1]])
17

18 sp_b = BSpline()
19 sp_b.approximate(line_b, ncp=5)
20 kn_bt = sp_b.get_knot_vector()
21

22 sp_t = BSpline()
23 sp_t.approximate(line_t, knots=kn_bt)

β€’ Create a new 2D SplineRegion2D object:

1.5. Examples 101


SfePy Documentation, Release version: 2022.1+git.f9873484

1 from sfepy.mesh.splinebox import SplineRegion2D


2

3 spb = SplineRegion2D([sp_b, sp_r, sp_t, sp_l], mesh.coors)

β€’ Move the control points:

1 spb.move_control_point(5, [-.2, .1])


2 spb.move_control_point(10, [-.3, .2])
3 spb.move_control_point(15, [-.1, .2])

β€’ Evaluate the new coordinates:

mesh.cmesh.coors[:] = spb.evaluate()

The figures below show the undeformed (left) and deformed (right) mesh and the control net.

1.5.6 Examples

acoustics

acoustics/acoustics.py

Description
Acoustic pressure distribution.
This example shows how to solve a problem in complex numbers, note the β€˜accoustic_pressure’ field definition.
Find 𝑝 such that:
∫︁ ∫︁ ∫︁ ∫︁
𝑐2 βˆ‡π‘ž Β· βˆ‡π‘ βˆ’ 𝑀2 π‘žπ‘ βˆ’ 𝑖𝑀𝑐 π‘žπ‘ = 𝑖𝑀𝑐2 πœŒπ‘£π‘› π‘ž, βˆ€π‘ž .
Ξ© Ξ© Ξ“π‘œπ‘’π‘‘ Γ𝑖𝑛

102 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Acoustic pressure distribution.

This example shows how to solve a problem in complex numbers, note the
'accoustic_pressure' field definition.

Find :math:`p` such that:

.. math::
c^2 \int_{\Omega} \nabla q \cdot \nabla p
- w^2 \int_{\Omega} q p
- i w c \int_{\Gamma_{out}} q p
= i w c^2 \rho v_n \int_{\Gamma_{in}} q
\;, \quad \forall q \;.
"""
from __future__ import absolute_import
from sfepy import data_dir

filename_mesh = data_dir + '/meshes/2d/special/two_rectangles.mesh'

v_n = 1.0 # m/s


(continues on next page)

1.5. Examples 103


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


w = 1000.0
c = 343.0 # m/s
rho = 1.55 # kg/m^3

options = {
'nls' : 'newton',
'ls' : 'ls',
}

materials = {
'one' : ({'one' : 1.0},),
}

regions = {
'Omega' : 'all',
'Gamma_in' : ('vertices in (x < 0.01)', 'facet'),
'Gamma_out' : ('vertices in (x > 0.99)', 'facet'),
}

fields = {
'accoustic_pressure' : ('complex', 1, 'Omega', 1),
}

variables = {
'p' : ('unknown field', 'accoustic_pressure', 0),
'q' : ('test field', 'accoustic_pressure', 'p'),
}

ebcs = {
}

integrals = {
'i' : 2,
}

equations = {
'Acoustic pressure' :
"""%s * dw_laplace.i.Omega( one.one, q, p )
- %s * dw_dot.i.Omega( q, p )
- %s * dw_dot.i.Gamma_out( q, p )
= %s * dw_integrate.i.Gamma_in( q )"""
% (c*c, w*w, 1j*w*c, 1j*w*c*c*rho*v_n)
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-1,
'eps_r' : 1.0,
'macheps' : 1e-16,
'lin_red' : 1e-1, # Linear system error < (eps_a * lin_red).
(continues on next page)

104 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'ls_red' : 0.1,
'ls_red_warp' : 0.001,
'ls_on' : 1.1,
'ls_min' : 1e-5,
'check' : 0,
'delta' : 1e-6,
})
}

acoustics/acoustics3d.py

Description
Acoustic pressure distribution in 3D.
Two Laplace equations, one in Ω1 , other in Ω2 , connected on the interface region Ξ“12 using traces of variables.
Find two complex acoustic pressures 𝑝1 , 𝑝2 such that:
∫︁ ∫︁
π‘˜ 2 π‘žπ‘ βˆ’ βˆ‡π‘ž Β· βˆ‡π‘
Ξ©
∫︁ ∫︁ ∫︁ Ω
βˆ’π‘–π‘€/𝑐 π‘žπ‘ + π‘–π‘€πœŒ/𝑍 π‘ž(𝑝2 βˆ’ 𝑝1 ) + π‘–π‘€πœŒ/𝑍 π‘ž(𝑝1 βˆ’ 𝑝2 )
Ξ“π‘œπ‘’π‘‘ Ξ“2
∫︁ Ξ“1
= π‘–π‘€πœŒ 𝑣𝑛 π‘ž , βˆ€π‘ž .
Γ𝑖𝑛

1.5. Examples 105


SfePy Documentation, Release version: 2022.1+git.f9873484

106 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Acoustic pressure distribution in 3D.

Two Laplace equations, one in :math:`\Omega_1`, other in


:math:`\Omega_2`, connected on the interface region :math:`\Gamma_{12}`
using traces of variables.

Find two complex acoustic pressures :math:`p_1`, :math:`p_2` such that:

.. math::
\int_{\Omega} k^2 q p - \int_{\Omega} \nabla q \cdot \nabla p \\
- i w/c \int_{\Gamma_{out}} q p
+ i w \rho/Z \int_{\Gamma_2} q (p_2 - p_1)
+ i w \rho/Z \int_{\Gamma_1} q (p_1 - p_2) \\
= i w \rho \int_{\Gamma_{in}} v_n q
\;, \quad \forall q \;.
"""

from __future__ import absolute_import


from sfepy import data_dir

(continues on next page)

1.5. Examples 107


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


filename_mesh = data_dir + '/meshes/3d/acoustics_mesh3d.mesh'

freq = 1200
v_n = 1.0 # m/s
c = 343.0 # m/s
rho = 1.55 # kg/m^3
R = 1000
w = 2.0 * freq

k1 = w / c
rhoc1 = rho * c

coef_k = ((1.0 + 0.1472 * (freq / R)**(-0.577))


+ 1j * (-0.1734 * (freq / R)**(-0.595)))
coef_r = ((1.0 + 0.0855 * (freq / R)**(-0.754))
+ 1j * (-0.0765 * (freq / R)**(-0.732)))

k2 = k1 * coef_k
rhoc2 = rhoc1 * coef_r

# perforation geometry parameters


tw = 0.9e-3
dh = 2.49e-3
por = 0.08

# acoustic impedance
Z = rho * c / por * (0.006 + 1j * k1 * (tw + 0.375 * dh
* (1 + rhoc2/rhoc1 * k2/k1)))

regions = {
'Omega' : 'all',
'Omega_1' : 'cells of group 1',
'Omega_2' : 'cells of group 2',
'Gamma_12' : ('r.Omega_1 *v r.Omega_2', 'facet'),
'Gamma_12_1' : ('copy r.Gamma_12', 'facet', 'Omega_1'),
'Gamma_12_2' : ('copy r.Gamma_12', 'facet', 'Omega_2'),
'Gamma_in' : ('vertices in (z < 0.001)', 'facet'),
'Gamma_out' : ('vertices in (z > 0.157)', 'facet'),
}

materials = {
}

fields = {
'accoustic_pressure_1' : ('complex', 'scalar', 'Omega_1', 1),
'accoustic_pressure_2' : ('complex', 'scalar', 'Omega_2', 1),
}

variables = {
'p_1' : ('unknown field', 'accoustic_pressure_1'),
'q_1' : ('test field', 'accoustic_pressure_1', 'p_1'),
'p_2' : ('unknown field', 'accoustic_pressure_2'),
(continues on next page)

108 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'q_2' : ('test field', 'accoustic_pressure_2', 'p_2'),
}

ebcs = {
}

integrals = {
'i' : 2,
}

equations = {
'Acoustic pressure' :
"""%s * dw_dot.i.Omega_1(q_1, p_1)
+ %s * dw_dot.i.Omega_2(q_2, p_2)
- dw_laplace.i.Omega_1(q_1, p_1)
- dw_laplace.i.Omega_2(q_2, p_2)
- %s * dw_dot.i.Gamma_out(q_1, p_1)
+ %s * dw_jump.i.Gamma_12_1(q_1, p_1, tr(p_2))
+ %s * dw_jump.i.Gamma_12_2(q_2, p_2, tr(p_1))
= %s * dw_integrate.i.Gamma_in(q_1)"""
% (k1*k1, k2*k2,
1j*k1,
1j*k1*rhoc1 / Z, 1j*k2*rhoc2 / Z,
1j*k1*rhoc1 * v_n)
}

options = {
'nls': 'newton',
'ls': 'ls',
'file_per_var': True,
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
'eps_r' : 1.0,
'macheps' : 1e-16,
'lin_red' : 1e-1,
'ls_red' : 0.1,
'ls_red_warp' : 0.001,
'ls_on' : 1.1,
'ls_min' : 1e-5,
'check' : 0,
'delta' : 1e-6,
})
}

1.5. Examples 109


SfePy Documentation, Release version: 2022.1+git.f9873484

acoustics/vibro_acoustic3d.py

Description
Vibro-acoustic problem
3D acoustic domain with 2D perforated deforming interface.
Master problem: defined in 3D acoustic domain (vibro_acoustic3d.py)
Slave subproblem: 2D perforated interface (vibro_acoustic3d_mid.py)
Master 3D problem - find 𝑝 (acoustic pressure) and 𝑔 (transversal acoustic velocity) such that:
∫︁ ∫︁ ∫︁ ∫︁ ∫︁ ∫︁
𝑐2 βˆ‡π‘ž Β· βˆ‡π‘ βˆ’ πœ” 2 π‘žπ‘ + π‘–πœ”π‘ π‘žπ‘ + π‘–πœ”π‘ π‘žπ‘ βˆ’ π‘–πœ”π‘2 (π‘ž + βˆ’ π‘ž βˆ’ )𝑔 = 2π‘–πœ”π‘ π‘ž 𝑝¯ , βˆ€π‘ž ,
Ξ© Ξ© Γ𝑖𝑛 Ξ“π‘œπ‘’π‘‘ Ξ“0 Γ𝑖𝑛
∫︁ ∫︁ ∫︁
βˆ’π‘–πœ” 𝑓 (𝑝+ βˆ’ π‘βˆ’ ) βˆ’ πœ” 2 𝐹 𝑓 𝑔 + πœ”2 𝐢𝑓 𝑀 = 0 , βˆ€π‘“ ,
Ξ“0 Ξ“0 Ξ“0

Slave 2D subproblem - find 𝑀 (plate deflection) and πœƒ (rotation) such that:


∫︁ ∫︁ ∫︁ ∫︁
2 2
πœ” 𝐢𝑧𝑔 βˆ’ πœ” 𝑆𝑧𝑀 + βˆ‡π‘§ Β· 𝐺 Β· βˆ‡π‘€ βˆ’ πœƒ Β· 𝐺 Β· βˆ‡π‘§ = 0 , βˆ€π‘§ ,
Ξ“0 Ξ“0 Ξ“0 Ξ“0
∫︁ ∫︁ ∫︁ ∫︁
βˆ’πœ” 2 π‘…πœˆ Β·πœƒ + π·π‘–π‘—π‘˜π‘™ 𝑒𝑖𝑗 (𝜈)π‘’π‘˜π‘™ (πœƒ) βˆ’ 𝜈 Β· 𝐺 Β· βˆ‡π‘€ + πœˆΒ·πΊΒ·πœƒ =0, βˆ€πœˆ ,
Ξ“0 Ξ“0 Ξ“0 Ξ“0

110 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

1.5. Examples 111


SfePy Documentation, Release version: 2022.1+git.f9873484

112 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Vibro-acoustic problem

3D acoustic domain with 2D perforated deforming interface.

*Master problem*: defined in 3D acoustic domain (``vibro_acoustic3d.py``)

*Slave subproblem*: 2D perforated interface (``vibro_acoustic3d_mid.py``)

Master 3D problem - find :math:`p` (acoustic pressure)


and :math:`g` (transversal acoustic velocity) such that:

.. math::
c^2 \int_{\Omega} \nabla q \cdot \nabla p
- \omega^2 \int_{\Omega} q p
+ i \omega c \int_{\Gamma_{in}} q p
+ i \omega c \int_{\Gamma_{out}} q p
- i \omega c^2 \int_{\Gamma_0} (q^+ - q^-) g
= 2i \omega c \int_{\Gamma_{in}} q \bar{p}
\;, \quad \forall q \;,

(continues on next page)

1.5. Examples 113


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


- i \omega \int_{\Gamma_0} f (p^+ - p^-)
- \omega^2 \int_{\Gamma_0} F f g
+ \omega^2 \int_{\Gamma_0} C f w
= 0
\;, \quad \forall f \;,

Slave 2D subproblem - find :math:`w` (plate deflection)


and :math:`\ul{\theta}` (rotation) such that:

.. math::
\omega^2 \int_{\Gamma_0} C z g
- \omega^2 \int_{\Gamma_0} S z w
+ \int_{\Gamma_0} \nabla z \cdot \ull{G} \cdot \nabla w
- \int_{\Gamma_0} \ul{\theta} \cdot \ull{G} \cdot \nabla z
= 0
\;, \quad \forall z \;,

- \omega^2 \int_{\Gamma_0} R\, \ul{\nu} \cdot \ul{\theta}


+ \int_{\Gamma_0} D_{ijkl} e_{ij}(\ul{\nu}) e_{kl}(\ul{\theta})
- \int_{\Gamma_0} \ul{\nu} \cdot \ull{G} \cdot \nabla w
+ \int_{\Gamma_0} \ul{\nu} \cdot \ull{G} \cdot \ul{\theta}
= 0
\;, \quad \forall \ul{\nu}
\;,
"""
from __future__ import absolute_import
from sfepy import data_dir, base_dir
filename_mesh = data_dir + '/meshes/3d/acoustic_wg.vtk'

sound_speed = 343.0
wave_num = 5.5
p_inc = 300

c = sound_speed
c2 = c**2
w = wave_num * c
w2 = w**2
wc = w * c
wc2 = w * c2

regions = {
'Omega1': 'cells of group 1',
'Omega2': 'cells of group 2',
'GammaIn': ('vertices of group 1', 'face'),
'GammaOut': ('vertices of group 2', 'face'),
'Gamma_aux': ('r.Omega1 *v r.Omega2', 'face'),
'Gamma0_1': ('copy r.Gamma_aux', 'face', 'Omega1'),
'Gamma0_2': ('copy r.Gamma_aux', 'face', 'Omega2'),
'aux_Left': ('vertices in (x < 0.001)', 'face'),
'aux_Right': ('vertices in (x > 0.299)', 'face'),
'Gamma0_1_Left': ('r.Gamma0_1 *v r.aux_Left', 'edge'),
'Gamma0_1_Right': ('r.Gamma0_1 *v r.aux_Right', 'edge'),
}
(continues on next page)

114 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

fields = {
'pressure1': ('complex', 'scalar', 'Omega1', 1),
'pressure2': ('complex', 'scalar', 'Omega2', 1),
'tvelocity': ('complex', 'scalar', 'Gamma0_1', 1),
'deflection': ('complex', 'scalar', 'Gamma0_1', 1),
}

variables = {
'p1': ('unknown field', 'pressure1', 0),
'q1': ('test field', 'pressure1', 'p1'),
'p2': ('unknown field', 'pressure2', 1),
'q2': ('test field', 'pressure2', 'p2'),
'g0': ('unknown field', 'tvelocity', 2),
'f0': ('test field', 'tvelocity', 'g0'),
'w': ('unknown field', 'deflection', 3),
'z': ('test field', 'deflection', 'w'),
}

ebcs = {
'fixed_l': ('Gamma0_1_Left', {'w.0': 0.0}),
'fixed_r': ('Gamma0_1_Right', {'w.0': 0.0}),
}

options = {
'file_per_var': True,
}

functions = {
}

materials = {
'ac' : ({'F': -2.064e+00, 'c': -1.064e+00}, ),
}

equations = {
'eq_1' : """
%e * dw_laplace.5.Omega1(q1, p1)
+ %e * dw_laplace.5.Omega2(q2, p2)
- %e * dw_dot.5.Omega1(q1, p1)
- %e * dw_dot.5.Omega2(q2, p2)
+ %s * dw_dot.5.GammaIn(q1, p1)
+ %s * dw_dot.5.GammaOut(q2, p2)
- %s * dw_dot.5.Gamma0_1(q1, g0)
+ %s * dw_dot.5.Gamma0_2(q2, tr(g0))
= %s * dw_integrate.5.GammaIn(q1)"""\
% (c2, c2, w2, w2,
1j * wc, 1j * wc,
1j * wc2, 1j * wc2,
2j * wc * p_inc),
'eq_2' : """
- %s * dw_dot.5.Gamma0_1(f0, p1)
(continues on next page)

1.5. Examples 115


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


+ %s * dw_dot.5.Gamma0_1(f0, tr(p2))
- %e * dw_dot.5.Gamma0_1(ac.F, f0, g0)
+ %e * dw_dot.5.Gamma0_1(ac.c, f0, w)
= 0"""\
% (1j * w, 1j * w, w2, w2),
}

solvers = {
'ls': ('ls.cm_pb',
{'others': [base_dir
+ '/examples/acoustics/vibro_acoustic3d_mid.py'],
'coupling_variables': ['g0', 'w'],
}),
'nls': ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-6,
'eps_r' : 1e-6,
})
}

dg

dg/advection_1D.py

Description
Transient advection equation in 1D solved using discontinous galerkin method.

𝑑𝑝
+ π‘Ž Β· 𝑑𝑝/𝑑π‘₯ = 0
𝑑𝑑
𝑝(𝑑, 0) = 𝑝(𝑑, 1)

Usage Examples

Run with simple.py script:

python simple.py sfepy/examples/dg/advection_1D.py

To view animated results use script/dg_plot_1D.py specifing name of the output in output/ folder, default is
dg/advection_1D:

python simple.py script/dg_plot_1D.py dg/advection_1D

script/dg_plot_1D.py also accepts full and relative paths:

python ./script/dg_plot_1D.py output/dg/advection_1D

116 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Transient advection equation in 1D solved using discontinous galerkin method.

.. math:: \frac{dp}{dt} + a \cdot dp/dx = 0

p(t,0) = p(t,1)

Usage Examples
--------------
Run with simple.py script::

python simple.py sfepy/examples/dg/advection_1D.py

To view animated results use ``script/dg_plot_1D.py`` specifing name of the


output in ``output/`` folder, default is ``dg/advection_1D``::

python simple.py script/dg_plot_1D.py dg/advection_1D

``script/dg_plot_1D.py`` also accepts full and relative paths::

(continues on next page)

1.5. Examples 117


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


python ./script/dg_plot_1D.py output/dg/advection_1D

"""
from sfepy.examples.dg.example_dg_common import *
from sfepy.discrete.dg.limiters import MomentLimiter1D

dim = 1

def define(filename_mesh=None,
approx_order=2,

adflux=0.0,
limit=True,

cw=None,
diffcoef=None,
diffscheme="symmetric",

cfl=0.4,
dt=None,
t1=0.1
):

t0 = 0
transient = True

mstart = 0
mend = 1

diffcoef = None
cw = None

example_name = "advection_1D"
dim = 1

if filename_mesh is None:
filename_mesh = get_gen_1D_mesh_hook(0, 1, 100)

materials = {
'a': ({'val': [1.0], '.flux': adflux},),

regions = {
'Omega': 'all',
'Gamma': ('vertices of surface', 'facet'),
'left': ('vertices in x == 0', 'vertex'),
'right': ('vertices in x == 1', 'vertex')
}

fields = {
(continues on next page)

118 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'f': ('real', 'scalar', 'Omega', str(approx_order) + 'd', 'DG', 'legendre')
}

variables = {
'p': ('unknown field', 'f', 0, 1),
'v': ('test field', 'f', 'p'),
}

dgebcs = {
'u_left': ('left', {'p.all': 0}),
'u_righ': ('right', {'p.all': 0}),
}

dgepbc_1 = {
'name' : 'u_rl',
'region': ['right', 'left'],
'dofs': {'p.all': 'p.all'},
'match': 'match_y_line',
}

integrals = {
'i': 2 * approx_order,
}

equations = {
'Advection': """
dw_dot.i.Omega(v, p)
- dw_s_dot_mgrad_s.i.Omega(a.val, p[-1], v)
+ dw_dg_advect_laxfrie_flux.i.Omega(a.flux, a.val, v, p[-1]) = 0
"""
}

solvers = {
"tss": ('ts.tvd_runge_kutta_3',
{"t0" : t0,
"t1" : t1,
'limiters': {"f": MomentLimiter1D} if limit else {}
}),
'nls': ('nls.newton', {}),
'ls' : ('ls.scipy_direct', {})
}

options = {
'ts' : 'tss',
'nls' : 'newton',
'ls' : 'ls',
'save_times' : 100,
'active_only' : False,
'pre_process_hook': get_cfl_setup(cfl)
if dt is None else
get_cfl_setup(dt=dt),
'output_dir' : 'output/dg/' + example_name,
(continues on next page)

1.5. Examples 119


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'output_format' : "vtk",
}

functions = {}

def local_register_function(fun):
try:
functions.update({fun.__name__: (fun,)})

except AttributeError: # Already a sfepy Function.


fun = fun.function
functions.update({fun.__name__: (fun,)})

return fun

def four_step_p(x):
"""
piecewise constant (-inf, 1.8],(1.8, a + 4](a+4, a + 5](a + 5, inf)
"""
return nm.piecewise(x,
[x <= mstart,
x <= mstart + .4,
mstart + .4 < x,
mstart + .5 <= x],
[0, 0, .5, 0])

@local_register_function
def get_ic(x, ic=None):
return four_step_p(x)

def analytic_sol(coors, t=None, uset=False):


x = coors[..., 0]
if uset:
res = get_ic(x[..., None] - t[None, ...])
return res # for animating transient problem

res = get_ic(x[..., None])


return res[..., 0]

@local_register_function
def sol_fun(ts, coors, mode="qp", **kwargs):
t = ts.time
if mode == "qp":
return {"p": analytic_sol(coors, t)[..., None, None]}

ics = {
'ic': ('Omega', {'p.0': 'get_ic'}),
}

return locals()

(continues on next page)

120 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

globals().update(define())

dg/advection_2D.py

Description
Transient advection equation in 2D solved by discontinous Galerkin method.
𝑑𝑝
+ π‘Ž Β· π‘”π‘Ÿπ‘Žπ‘‘ 𝑝 = 0
𝑑𝑑

Usage Examples

Run with simple.py script:

python simple.py examples/dg/advection_2D.py

Results are saved to output/dg/advection_2D folder by default as .msh files, the best way to view them is through GMSH
(http://gmsh.info/) version 4.6 or newer. Start GMSH and use File | Open menu or Crtl + O shortcut, navigate to the
output folder, select all .msh files and hit Open, all files should load as one item in Post-processing named p_cell_nodes.
GMSH is capable of rendering high order approximations in individual elements, to modify fidelity of rendering, double
click the displayed mesh, quick options menu should pop up, click on All view options.... This brings up the
Options window with View [0] selected in left column. Under the tab General ensure that Adapt visualization
grid is ticked, then you can adjust Maximum recursion depth and `Target visualization error to tune the
visualization. To see visualization elements (as opposed to mesh elements) go to Visibility tab and tick Draw
element outlines, this option is also available from quick options menu as View element outlines or under
shortcut Alt+E. In the quick options menu, you can also modify normal raise by clicking View Normal Raise to see
solution rendered as surface above the mesh. Note that for triangular meshes normal raise -1 produces expected raise
above the mesh. This is due to the opposite orientation of the reference elements in GMSH and Sfepy and might get
patched in the future.
source code

r"""
Transient advection equation in 2D solved by discontinous Galerkin method.

.. math:: \frac{dp}{dt} + a\cdot grad\,p = 0

Usage Examples
--------------

Run with simple.py script::

python simple.py sfepy/examples/dg/advection_2D.py

Results are saved to output/dg/advection_2D folder by default as ``.msh`` files,


the best way to view them is through GMSH (http://gmsh.info/) version 4.6 or
newer. Start GMSH and use ``File | Open`` menu or Crtl + O shortcut, navigate to
the output folder, select all ``.msh`` files and hit Open, all files should load
as one item in Post-processing named p_cell_nodes.
(continues on next page)

1.5. Examples 121


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

GMSH is capable of rendering high order approximations in individual elements,


to modify fidelity of rendering, double click the displayed mesh, quick options
menu should pop up, click on ``All view options...``. This brings up the Options
window with ``View [0]`` selected in left column. Under the tab ``General``
ensure that ``Adapt visualization grid`` is ticked, then you can adjust
``Maximum recursion depth`` and ```Target visualization error`` to tune
the visualization. To see visualization elements (as opposed to mesh elements)
go to ``Visibility`` tab and tick ``Draw element outlines``, this option is also
available from quick options menu as ``View element outlines`` or under shortcut
``Alt+E``. In the quick options menu, you can also modify normal raise by
clicking ``View Normal Raise`` to see solution rendered as surface above the
mesh. Note that for triangular meshes normal raise -1 produces expected raise
above the mesh. This is due to the opposite orientation of the reference
elements in GMSH and Sfepy and might get patched in the future.
"""
from sfepy.examples.dg.example_dg_common import *
from sfepy.discrete.dg.limiters import MomentLimiter2D

mesh_center = (0.5, 0.25)


mesh_size = (1.0, 0.5)

def define(filename_mesh=None,
approx_order=2,

adflux=0,
limit=True,

cw=None,
diffcoef=None,
diffscheme="symmetric",

cfl=0.4,
dt=None,
t1=0.01

):

example_name = "advection_2D"
dim = 2

diffcoef = None
cw = None

if filename_mesh is None:
filename_mesh = get_gen_block_mesh_hook((1., 1.), (20, 20), (.5, .5))

t0 = 0.

angle = 0
# get_common(approx_order, cfl, t0, t1, None, get_ic)
rotm = nm.array([[nm.cos(angle), -nm.sin(angle)],
(continues on next page)

122 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


[nm.sin(angle), nm.cos(angle)]])
velo = nm.sum(rotm.T * nm.array([1., 0.]), axis=-1)[:, None]
materials = {
'a': ({'val': [velo], '.flux': adflux},),
}

regions = {
'Omega' : 'all',
'left': ('vertices in x == 0', 'edge'),
'right': ('vertices in x == 1', 'edge'),
'top': ('vertices in y == 1', 'edge'),
'bottom': ('vertices in y == 0', 'edge')
}

fields = {
'f': ('real', 'scalar', 'Omega', str(approx_order) + 'd', 'DG', 'legendre') #
}

variables = {
'p': ('unknown field', 'f', 0, 1),
'v': ('test field', 'f', 'p'),
}

def gsmooth(x):
"""
.. :math: C_0^{\inf}
"""
return .3 * nm.piecewise(x, [x <= 0.1, x >= 0.1, .3 < x],
[0, lambda x:
nm.exp(1 / ((10 * (x - .2)) ** 2 - 1) + 1),
0])

def analytic_sol(coors, t):


x_1 = coors[..., 0]
x_2 = coors[..., 1]
sin = nm.sin
pi = nm.pi
exp = nm.exp
# res = four_step_u(x_1) * four_step_u(x_2)
res = gsmooth(x_1) * gsmooth(x_2)
return res

@local_register_function
def sol_fun(ts, coors, mode="qp", **kwargs):
t = ts.time
if mode == "qp":
return {"p": analytic_sol(coors, t)[..., None, None]}

def get_ic(x, ic=None):


return gsmooth(x[..., 0:1]) * gsmooth(x[..., 1:])

(continues on next page)

1.5. Examples 123


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


functions = {
'get_ic': (get_ic,)
}

ics = {
'ic': ('Omega', {'p.0': 'get_ic'}),
}

dgepbc_1 = {
'name': 'u_rl',
'region': ['right', 'left'],
'dofs': {'p.all': 'p.all'},
'match': 'match_y_line',
}

integrals = {
'i': 3 * approx_order,
}

equations = {
'Advection': """
dw_dot.i.Omega(v, p)
- dw_s_dot_mgrad_s.i.Omega(a.val, p[-1], v)
+ dw_dg_advect_laxfrie_flux.i.Omega(a.flux, a.val, v, p[-1]) = 0
"""
}

solvers = {
"tss": ('ts.tvd_runge_kutta_3',
{"t0" : t0,
"t1" : t1,
'limiters': {"f": MomentLimiter2D} if limit else {}}),
'nls': ('nls.newton',{}),
'ls' : ('ls.scipy_direct', {})
}

options = {
'ts' : 'tss',
'nls' : 'newton',
'ls' : 'ls',
'save_times' : 100,
'active_only' : False,
'output_dir' : 'output/dg/' + example_name,
'output_format' : 'msh',
'file_format' : 'gmsh-dg',
'pre_process_hook': get_cfl_setup(cfl) if dt is None else get_cfl_setup(dt=dt)
}

return locals()

globals().update(define())

124 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

dg/advection_diffusion_2D.py

Description
Static advection-diffusion equation in 2D solved by discontinous Galerkin method.

π‘Ž Β· π‘”π‘Ÿπ‘Žπ‘‘ 𝑝 βˆ’ 𝑑𝑖𝑣(π‘”π‘Ÿπ‘Žπ‘‘ 𝑝) = 0

Based on
Antonietti, P., & Quarteroni, A. (2013). Numerical performance of discontinuous and stabilized continuous
Galerkin methods for convection-diffusion problems.

Usage Examples

Run with simple.py script:

python simple.py examples/dg/advection_diffusion_2D.py

Results are saved to output/dg/advection_diffusion_2D folder by default as ` .msh` files, the best way to view them is
through GMSH (http://gmsh.info/) version 4.6 or newer. Start GMSH and use File | Open menu or Crtl + O shortcut,
navigate to the output folder, select all .msh files and hit Open, all files should load as one item in Post-processing named
p_cell_nodes.
GMSH is capable of rendering high order approximations in individual elements, to modify fidelity of rendering, double
click the displayed mesh, quick options menu should pop up, click on All view options.... This brings up the
Options window with View [0] selected in left column. Under the tab General ensure that Adapt visualization
grid is ticked, then you can adjust Maximum recursion depth and `Target visualization error to tune the
visualization. To see visualization elements (as opposed to mesh elements) go to Visibility tab and tick Draw
element outlines, this option is also available from quick options menu as View element outlines or under
shortcut Alt+E. In the quick options menu, you can also modify normal raise by clicking View Normal Raise to see
solution rendered as surface above the mesh. Note that for triangular meshes normal raise -1 produces expected raise
above the mesh. This is due to the opposite orientation of the reference elements in GMSH and Sfepy and might get
patched in the future.
source code

r"""
Static advection-diffusion equation in 2D solved by discontinous Galerkin method.

.. math:: a \cdot grad\, p - div(grad\,p) = 0

Based on

Antonietti, P., & Quarteroni, A. (2013). Numerical performance of discontinuous


and stabilized continuous Galerkin methods for convection-diffusion problems.

Usage Examples
--------------

Run with simple.py script::

python simple.py sfepy/examples/dg/advection_diffusion_2D.py


(continues on next page)

1.5. Examples 125


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

Results are saved to output/dg/advection_diffusion_2D folder by default as `


`.msh`` files, the best way to view them is through GMSH (http://gmsh.info/)
version 4.6 or newer. Start GMSH and use ``File | Open`` menu or Crtl + O
shortcut, navigate to the output folder, select all ``.msh`` files and hit Open,
all files should load as one item in Post-processing named p_cell_nodes.

GMSH is capable of rendering high order approximations in individual elements,


to modify fidelity of rendering, double click the displayed mesh, quick options
menu should pop up, click on ``All view options...``. This brings up the Options
window with ``View [0]`` selected in left column. Under the tab ``General``
ensure that ``Adapt visualization grid`` is ticked, then you can adjust
``Maximum recursion depth`` and ```Target visualization error`` to tune
the visualization. To see visualization elements (as opposed to mesh elements)
go to ``Visibility`` tab and tick ``Draw element outlines``, this option is also
available from quick options menu as ``View element outlines`` or under shortcut
``Alt+E``. In the quick options menu, you can also modify normal raise by
clicking ``View Normal Raise`` to see solution rendered as surface above the
mesh. Note that for triangular meshes normal raise -1 produces expected raise
above the mesh. This is due to the opposite orientation of the reference
elements in GMSH and Sfepy and might get patched in the future.
"""

from sfepy.examples.dg.example_dg_common import *

def define(filename_mesh=None,
approx_order=3,

adflux=0,
limit=False,

cw=1000,
diffcoef=1,
diffscheme="symmetric",

cfl=None,
dt=None,
):

cfl = None
dt = None

functions = {}
def local_register_function(fun):
try:
functions.update({fun.__name__: (fun,)})

except AttributeError: # Already a sfepy Function.


fun = fun.function
functions.update({fun.__name__: (fun,)})

(continues on next page)

126 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


return fun

example_name = "advection_diffusion_2D"
dim = 2

if filename_mesh is None:
filename_mesh = get_gen_block_mesh_hook((1., 1.), (20, 20), (.5, .5))

velo = [1., 1.]

angle = 0.0 # - nm.pi / 5


rotm = nm.array([[nm.cos(angle), -nm.sin(angle)],
[nm.sin(angle), nm.cos(angle)]])
velo = nm.sum(rotm.T * nm.array(velo), axis=-1)[:, None]

regions = {
'Omega' : 'all',
'left' : ('vertices in x == 0', 'edge'),
'right': ('vertices in x == 1', 'edge'),
'top' : ('vertices in y == 1', 'edge'),
'bottom': ('vertices in y == 0', 'edge')
}

fields = {
'f': ('real', 'scalar', 'Omega', str(approx_order) + 'd', 'DG', 'legendre')
}

variables = {
'p': ('unknown field', 'f', 0),
'v': ('test field', 'f', 'p'),
}

integrals = {
'i': 2 * approx_order,
}

@local_register_function
def bc_funs(ts, coors, bc, problem):
# return 2*coors[..., 1]
t = ts.dt*ts.step
x_1 = coors[..., 0]
x_2 = coors[..., 1]
res = nm.zeros(nm.shape(x_1))

sin = nm.sin
cos = nm.cos
exp = nm.exp
pi = nm.pi

if bc.diff == 0:
if "left" in bc.name:
(continues on next page)

1.5. Examples 127


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


res[:] = 0
elif "right" in bc.name:
res[:] = 0
elif "bottom" in bc.name:
res[:] = 0 #-2*sin(2*pi*x_1)
elif "top" in bc.name:
res[:] = 0

elif bc.diff == 1:
if "left" in bc.name:
res = nm.stack((-2*pi*(x_2**2 - x_2),
res),
axis=-2)
elif "right" in bc.name:
res = nm.stack((-2*pi*(x_2**2 - x_2), res,),
axis=-2)
elif "bot" in bc.name:
res = nm.stack((res,
sin(2*pi*x_1)),
axis=-2)
elif "top" in bc.name:
res = nm.stack((res,
-sin(2*pi*x_1)),
axis=-2)

return res

@local_register_function
def source_fun(ts, coors, mode="qp", **kwargs):
# t = ts.dt * ts.step
eps = diffcoef
sin = nm.sin
cos = nm.cos
exp = nm.exp
sqrt = nm.sqrt
pi = nm.pi
if mode == "qp":
x_1 = coors[..., 0]
x_2 = coors[..., 1]
res = -2*pi*(x_2**2 - x_2)*cos(2*pi*x_1)\
- 2*(2*pi**2*(x_2**2 - x_2)*sin(2*pi*x_1) - sin(2*pi*x_1))*eps\
- (2*x_2 - 1)*sin(2*pi*x_1)
return {"val": res[..., None, None]}

def analytic_sol(coors, t):


x_1 = coors[..., 0]
x_2 = coors[..., 1]
sin = nm.sin
pi = nm.pi
res = -(x_2 ** 2 - x_2) * sin(2 * pi * x_1)
(continues on next page)

128 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


return res

@local_register_function
def sol_fun(ts, coors, mode="qp", **kwargs):
t = ts.time
if mode == "qp":
return {"p": analytic_sol(coors, t)[..., None, None]}

dgebcs = {
'u_left' : ('left', {'p.all': "bc_funs", 'grad.p.all' : "bc_funs"}),
'u_top' : ('top', {'p.all': "bc_funs", 'grad.p.all' : "bc_funs"}),
'u_bot' : ('bottom', {'p.all': "bc_funs", 'grad.p.all' : "bc_funs"}),
'u_right': ('right', {'p.all': "bc_funs", 'grad.p.all' : "bc_funs"}),
}

materials = {
'a' : ({'val': [velo], '.flux': adflux},),
'D' : ({'val': [diffcoef], '.cw': cw},),
'g' : 'source_fun'
}

equations = {
'balance': """
- dw_s_dot_mgrad_s.i.Omega(a.val, p, v)
+ dw_dg_advect_laxfrie_flux.i.Omega(a.flux, a.val, v, p)
"""
+
" + dw_laplace.i.Omega(D.val, v, p) " +
diffusion_schemes_implicit[diffscheme] +
" + dw_dg_interior_penalty.i.Omega(D.val, D.cw, v, p)" +
" - dw_volume_lvf.i.Omega(g.val, v)" +
"= 0"
}

solver_0 = {
'name' : 'ls',
'kind' : 'ls.scipy_direct',
}

solver_1 = {
'name' : 'newton',
'kind' : 'nls.newton',

'i_max' : 5,
'eps_a' : 1e-8,
'eps_r' : 1.0,
'macheps' : 1e-16,
'lin_red' : 1e-2, # Linear system error < (eps_a * lin_red).
'ls_red' : 0.1,
'ls_red_warp' : 0.001,
(continues on next page)

1.5. Examples 129


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'ls_on' : 0.99999,
'ls_min' : 1e-5,
'check' : 0,
'delta' : 1e-6,
}

options = {
'nls' : 'newton',
'ls' : 'ls',
'output_dir' : 'output/dg/' + example_name,
'output_format' : 'msh',
'file_format' : 'gmsh-dg'
}
return locals()

globals().update(define())
pass

dg/burgers_2D.py

Description
Burgers equation in 2D solved using discontinous Galerkin method

𝑑𝑝
+ 𝑑𝑖𝑣 𝑓 (𝑝) βˆ’ 𝑑𝑖𝑣(π‘”π‘Ÿπ‘Žπ‘‘ 𝑝) = 0
𝑑𝑑
Based on
Kučera, V. (n.d.). Higher order methods for the solution of compressible flows. Charles University. p. 21 eq. (1.39)

Usage Examples

Run with simple.py script:

python simple.py examples/dg/burgers_2D.py

Results are saved to output/dg/burgers_2D folder by default as .msh files, the best way to view them is through GMSH
(http://gmsh.info/) version 4.6 or newer. Start GMSH and use File | Open menu or Crtl + O shortcut, navigate to the
output folder, select all .msh files and hit Open, all files should load as one item in Post-processing named p_cell_nodes.
GMSH is capable of rendering high order approximations in individual elements, to modify fidelity of rendering, double
click the displayed mesh, quick options menu should pop up, click on All view options.... This brings up the
Options window with View [0] selected in left column. Under the tab General ensure that Adapt visualization
grid is ticked, then you can adjust Maximum recursion depth and `Target visualization error to tune the
visualization. To see visualization elements (as opposed to mesh elements) go to Visibility tab and tick Draw
element outlines, this option is also available from quick options menu as View element outlines or under
shortcut Alt+E. In the quick options menu, you can also modify normal raise by clicking View Normal Raise to see
solution rendered as surface above the mesh. Note that for triangular meshes normal raise -1 produces expected raise
above the mesh. This is due to the opposite orientation of the reference elements in GMSH and Sfepy and might get
patched in the future.

130 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Burgers equation in 2D solved using discontinous Galerkin method

.. math:: \frac{dp}{dt} + div\,f(p) - div(grad\,p) = 0

Based on

Kučera, V. (n.d.). Higher order methods for the solution of compressible flows.
Charles University. p. 21 eq. (1.39)

Usage Examples
--------------

Run with simple.py script::

python simple.py sfepy/examples/dg/burgers_2D.py

Results are saved to output/dg/burgers_2D folder by default as ``.msh`` files,


the best way to view them is through GMSH (http://gmsh.info/) version 4.6 or
newer. Start GMSH and use ``File | Open`` menu or Crtl + O shortcut, navigate to
the output folder, select all ``.msh`` files and hit Open, all files should load
as one item in Post-processing named p_cell_nodes.

GMSH is capable of rendering high order approximations in individual elements,


to modify fidelity of rendering, double click the displayed mesh, quick options
menu should pop up, click on ``All view options...``. This brings up the Options
window with ``View [0]`` selected in left column. Under the tab ``General``
ensure that ``Adapt visualization grid`` is ticked, then you can adjust
``Maximum recursion depth`` and ```Target visualization error`` to tune
the visualization. To see visualization elements (as opposed to mesh elements)
go to ``Visibility`` tab and tick ``Draw element outlines``, this option is also
available from quick options menu as ``View element outlines`` or under shortcut
``Alt+E``. In the quick options menu, you can also modify normal raise by
clicking ``View Normal Raise`` to see solution rendered as surface above the
mesh. Note that for triangular meshes normal raise -1 produces expected raise
above the mesh. This is due to the opposite orientation of the reference
elements in GMSH and Sfepy and might get patched in the future.
"""

from sfepy.examples.dg.example_dg_common import *


from sfepy import data_dir

from sfepy.discrete.dg.limiters import MomentLimiter2D, IdentityLimiter

mesh_center = (0, 0)
mesh_size = (2, 2)

def define(filename_mesh=None,
approx_order=2,
(continues on next page)

1.5. Examples 131


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

adflux=0,
limit=False,

cw=10,
diffcoef=0.002,
diffscheme="symmetric",

cfl=None,
dt=1e-5,
t1=0.01
):

functions = {}
def local_register_function(fun):
try:
functions.update({fun.__name__: (fun,)})

except AttributeError: # Already a sfepy Function.


fun = fun.function
functions.update({fun.__name__: (fun,)})

return fun

example_name = "burgers_2D"
dim = 2

if filename_mesh is None:
filename_mesh = data_dir + "/meshes/2d/square_tri2.mesh"

t0 = 0.
if dt is None and cfl is None:
dt = 1e-5

velo = [1., 1.]

angle = 0 # - nm.pi / 5
rotm = nm.array([[nm.cos(angle), -nm.sin(angle)],
[nm.sin(angle), nm.cos(angle)]])
velo = nm.sum(rotm.T * nm.array(velo), axis=-1)[:, None]
burg_velo = velo.T / nm.linalg.norm(velo)

regions = {
'Omega': 'all',
'left' : ('vertices in x == -1', 'edge'),
'right': ('vertices in x == 1', 'edge'),
'top' : ('vertices in y == 1', 'edge'),
'bottom': ('vertices in y == -1', 'edge')
}

fields = {
(continues on next page)

132 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'f': ('real', 'scalar', 'Omega',
str(approx_order) + 'd', 'DG', 'legendre')
}

variables = {
'p': ('unknown field', 'f', 0, 1),
'v': ('test field', 'f', 'p'),
}

integrals = {
'i': 5,
}

def analytic_sol(coors, t):


x_1 = coors[..., 0]
x_2 = coors[..., 1]
sin = nm.sin
pi = nm.pi
exp = nm.exp
res = -(exp(-t) - 1)*(sin(5*x_1*x_2) + sin(-4*x_1*x_2 + 4*x_1 + 4*x_2))
return res

@local_register_function
def sol_fun(ts, coors, mode="qp", **kwargs):
t = ts.time
if mode == "qp":
return {"p": analytic_sol(coors, t)[..., None, None]}

@local_register_function
def bc_funs(ts, coors, bc, problem):
# return 2*coors[..., 1]
t = ts.dt*ts.step
x_1 = coors[..., 0]
x_2 = coors[..., 1]
sin = nm.sin
cos = nm.cos
exp = nm.exp
if bc.diff == 0:
if "left" in bc.name:
res = -(exp(-t) - 1)*(sin(-5*x_2) + sin(8*x_2 - 4))
elif "bottom" in bc.name:
res = -(exp(-t) - 1) * (sin(-5 * x_1) + sin(8 * x_1 - 4))
elif "right" in bc.name:
res = -(exp(-t) - 1)*(sin(4) + sin(5*x_2))
elif "top" in bc.name:
res = -(exp(-t) - 1)*(sin(4) + sin(5*x_1))

elif bc.diff == 1:
if "left" in bc.name:
res = nm.stack(((4*(x_2 - 1)*cos(4) - 5*x_2*cos(5*x_2))*
(exp(-t) - 1),
-5*(exp(-t) - 1)*cos(5*x_2)),
(continues on next page)

1.5. Examples 133


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


axis=-2)
elif "bottom" in bc.name:
res = nm.stack(((5*cos(-5*x_1) - 8*cos(8*x_1 - 4))*(exp(-t) - 1),
-(5*x_1*cos(-5*x_1) - 4*(x_1 - 1)*cos(8*x_1 - 4))*
(exp(-t) - 1)),
axis=-2)

elif "right" in bc.name:


res = nm.stack(((4*(x_2 - 1)*cos(4) - 5*x_2*cos(5*x_2))*
(exp(-t) - 1),
-5*(exp(-t) - 1)*cos(5*x_2)),
axis=-2)
elif "top" in bc.name:
res = nm.stack((-5*(exp(-t) - 1)*cos(5*x_1),
(4*(x_1 - 1)*cos(4) - 5*x_1*cos(5*x_1))*
(exp(-t) - 1)),
axis=-2)

return res

@local_register_function
def source_fun(ts, coors, mode="qp", **kwargs):
if mode == "qp":
t = ts.dt * ts.step
x_1 = coors[..., 0]
x_2 = coors[..., 1]
sin = nm.sin
cos = nm.cos
exp = nm.exp
res = (
+ (5 * x_1 * cos(5 * x_1 * x_2)
- 4 * (x_1 - 1) * cos(4 * x_1 * x_2 - 4 * x_1 - 4 * x_2)) *
(exp(-t) - 1) ** 2 * (sin(5 * x_1 * x_2)
- sin(4 * x_1 * x_2 - 4 * x_1 - 4 * x_2))
+ (5 * x_2 * cos(5 * x_1 * x_2)
- 4 * (x_2 - 1) * cos(4 * x_1 * x_2 - 4 * x_1 - 4 * x_2)) *
(exp(-t) - 1) ** 2 * (sin(5 * x_1 * x_2)
- sin(4 * x_1 * x_2 - 4 * x_1 - 4 * x_2))
- diffcoef *
((25 * x_1 ** 2 * sin(5 * x_1 * x_2) - 16 * (x_1 - 1) ** 2 *
sin(4 * x_1 * x_2 - 4 * x_1 - 4 * x_2)) * (exp(-t) - 1)
+ (25 * x_2 ** 2 * sin(5 * x_1 * x_2) - 16 * (x_2 - 1) ** 2 *
sin(4 * x_1 * x_2 - 4 * x_1 - 4 * x_2)) * (exp(-t) - 1))
+ (sin(5 * x_1 * x_2) - sin(4 * x_1 * x_2 - 4 * x_1 - 4 * x_2))*
exp(-t)
)
return {"val": res[..., None, None]}

def adv_fun(p):
vu = velo.T * p[..., None]
return vu

(continues on next page)

134 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


def adv_fun_d(p):
v1 = velo.T * nm.ones(p.shape + (1,))
return v1

def burg_fun(p):
vu = .5*burg_velo * p[..., None] ** 2
return vu

def burg_fun_d(p):
v1 = burg_velo * p[..., None]
return v1

materials = {
'a' : ({'val': [velo], '.flux':adflux},),
'D' : ({'val': [diffcoef], '.Cw': cw},),
'g' : 'source_fun'
}

ics = {
'ic': ('Omega', {'p.0': 0}),
}

dgebcs = {
'u_left' : ('left', {'p.all': 'bc_funs', 'grad.p.all': 'bc_funs'}),
'u_right' : ('right', {'p.all': 'bc_funs', 'grad.p.all': 'bc_funs'}),
'u_bottom' : ('bottom', {'p.all': 'bc_funs', 'grad.p.all': 'bc_funs'}),
'u_top' : ('top', {'p.all': 'bc_funs', 'grad.p.all': 'bc_funs'}),
}

equations = {
'balance':
"dw_dot.i.Omega(v, p)" +
# non-linear hyperbolic terms
" - dw_ns_dot_grad_s.i.Omega(burg_fun, burg_fun_d, p[-1], v)" +
" + dw_dg_nonlinear_laxfrie_flux.i.Omega(a.flux, burg_fun, burg_fun_d, v, p[-1])
Λ“β†’" +

# diffusion
" + dw_laplace.i.Omega(D.val, v, p[-1])" +
diffusion_schemes_explicit[diffscheme] +
" - dw_dg_interior_penalty.i.Omega(D.val, D.Cw, v, p[-1])"
# source
+ " - dw_volume_lvf.i.Omega(g.val, v)"
" = 0"
}

solvers = {
"tss.tvd_runge_kutta_3": ('ts.tvd_runge_kutta_3',
{"t0": t0,
"t1": t1,
'limiters': {
"f": MomentLimiter2D} if limit else {}}),
(continues on next page)

1.5. Examples 135


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


"tss.euler": ('ts.euler',
{"t0" : t0,
"t1" : t1,
'limiters': {"f": MomentLimiter2D} if limit else {}}),
'nls': ('nls.newton', {}),
'ls' : ('ls.scipy_direct', {})
}

options = {
'ts' : 'tss.euler',
'nls' : 'nls.newton',
'ls' : 'ls.mumps',
'save_times' : 100,
'output_dir' : 'output/dg/' + example_name,
'output_format' : 'msh',
'file_format' : 'gmsh-dg',
'pre_process_hook': get_cfl_setup(CFL=cfl, dt=dt)
}

return locals()

globals().update(define())

dg/example_dg_common.py

Description
Functions common to DG examples
source code

"""
Functions common to DG examples
"""
import os
from glob import glob

import numpy as nm

from sfepy.base.base import output


from sfepy.discrete.fem import Mesh
from sfepy.discrete.fem.meshio import UserMeshIO
from sfepy.mesh.mesh_generators import gen_block_mesh

diffusion_schemes_implicit = {
"symmetric":
" + dw_dg_diffusion_flux.i.Omega(D.val, p, v)"
+ " + dw_dg_diffusion_flux.i.Omega(D.val, v, p)",
"non-symmetric":
" + dw_dg_diffusion_flux.i.Omega(D.val, p, v)"
+ " - dw_dg_diffusion_flux.i.Omega(D.val, v, p)",
(continues on next page)

136 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


"incomplete":
" + dw_dg_diffusion_flux.i.Omega(D.val, p, v)"}

diffusion_schemes_explicit = {
"symmetric":
" - dw_dg_diffusion_flux.i.Omega(D.val, p[-1], v)"
+ " - dw_dg_diffusion_flux.i.Omega(D.val, v, p[-1])",
"non-symmetric":
" - dw_dg_diffusion_flux.i.Omega(D.val, p[-1], v)"
+ " + dw_dg_diffusion_flux.i.Omega(D.val, v, p[-1])",
"incomplete":
" - dw_dg_diffusion_flux.i.Omega(D.val, p[-1], v)"}

functions = {}
def local_register_function(fun):
try:
functions.update({fun.__name__: (fun,)})

except AttributeError: # Already a sfepy Function.


fun = fun.function
functions.update({fun.__name__: (fun,)})

return fun

def get_cfl_setup(CFL=None, dt=None):


"""
Provide either CFL or dt to create preprocess hook that sets up
Courant-Friedrichs-Levi stability condition for either advection or
diffusion.

Parameters
----------
CFL : float, optional
dt: float, optional

Returns
-------
setup_cfl_condition : callable
expects sfepy.discrete.problem as argument

"""

if CFL is None and dt is None:


raise ValueError("Specifiy either CFL or dt in CFL setup")

def setup_cfl_condition(problem):
"""
Sets up CFL condition for problem ts_conf in problem

Parameters
----------
(continues on next page)

1.5. Examples 137


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


problem : discrete.problem.Problem
"""
ts_conf = problem.ts_conf
mesh = problem.domain.mesh
dim = mesh.dim
first_field = list(problem.fields.values())[0]
first_field_name = list(problem.fields.keys())[0]
approx_order = first_field.approx_order
mats = problem.create_materials(['a', 'D'])
try:
# make this more general?
# maybe require material name in parameter
velo = problem.conf_materials['material_a__0'].values["val"]
max_velo = nm.max(nm.linalg.norm(velo))
except KeyError:
max_velo = 1

try:
# make this more general?
# maybe require material name in parameter
diffusion = problem.conf_materials['material_D__0'].values["val"]
max_diffusion = nm.max(nm.linalg.norm(diffusion))
except KeyError:
max_diffusion = None

dx = nm.min(problem.domain.mesh.cmesh.get_volumes(dim))

output("Preprocess hook - setup_cfl_condition:...")


output("Approximation order of field {}({}) is {}"
.format(first_field_name, first_field.family_name, approx_order))
output("Space divided into {0} cells, {1} steps, step size {2}"
.format(mesh.n_el, len(mesh.coors), dx))

if dt is None:
adv_dt = get_cfl_advection(max_velo, dx, approx_order, CFL)
diff_dt = get_cfl_diffusion(max_diffusion, dx, approx_order, CFL)
_dt = min(adv_dt, diff_dt)
else:
output("CFL coefficient {0} ignored, dt specified directly"
.format(CFL))
_dt = dt

tn = int(nm.ceil((ts_conf.t1 - ts_conf.t0) / _dt))


dtdx = _dt / dx

ts_conf.dt = _dt
ts_conf.n_step = tn
ts_conf.cour = max_velo * dtdx

output("Time divided into {0} nodes, {1} steps, step size is {2}"
.format(tn - 1, tn, _dt))
output("Courant number c = max(norm(a)) * dt/dx = {0}"
(continues on next page)

138 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


.format(ts_conf.cour))
output("Time stepping solver is {}".format(ts_conf.kind))
output("... CFL setup done.")

return setup_cfl_condition

def get_cfl_advection(max_velo, dx, approx_order, CFL):


"""

Parameters
----------
max_velo : float
dx : float
approx_order : int
CFL : CFL

Returns
-------
dt : float
"""
order_corr = 1. / (2 * approx_order + 1)

dt = dx / max_velo * CFL * order_corr

if not (nm.isfinite(dt)):
dt = 1
output(("CFL advection: CFL coefficient was {0} " +
"and order correction 1/{1} = {2}")
.format(CFL, (2 * approx_order + 1), order_corr))
output("CFL advection: resulting dt={}".format((dt)))
return dt

def get_cfl_diffusion(max_diffusion, dx, approx_order, CFL,


do_order_corr=False):
"""

Parameters
----------
max_diffusion : float
dx : float
approx_order : int
CFL : float
do_order_corr : bool

Returns
-------
dt : float
"""
(continues on next page)

1.5. Examples 139


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


if max_diffusion is None:
return 1

if do_order_corr:
order_corr = 1. / (2 * approx_order + 1)
else:
order_corr = 1

dt = dx**2 / max_diffusion * CFL * order_corr

if not (nm.isfinite(dt)):
dt = 1
output(("CFL diffusion: CFL coefficient was {0} " +
"and order correction 1/{1} = {2}")
.format(CFL, (2 * approx_order + 1), order_corr))
output("CFL diffusion: resulting dt={}".format(dt))
return dt

def get_gen_1D_mesh_hook(XS, XE, n_nod):


"""

Parameters
----------
XS : float
leftmost coordinate
XE : float
rightmost coordinate
n_nod : int
number of nodes, number of cells is then n_nod - 1

Returns
-------
mio : UserMeshIO instance
"""
def mesh_hook(mesh, mode):
"""
Generate the 1D mesh.
"""
if mode == 'read':

coors = nm.linspace(XS, XE, n_nod).reshape((n_nod, 1))


conn = nm.arange(n_nod, dtype=nm.int32) \
.repeat(2)[1:-1].reshape((-1, 2))
mat_ids = nm.zeros(n_nod - 1, dtype=nm.int32)
descs = ['1_2']

mesh = Mesh.from_data('uniform_1D{}'.format(n_nod), coors, None,


[conn], [mat_ids], descs)
return mesh

elif mode == 'write':


(continues on next page)

140 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


pass

mio = UserMeshIO(mesh_hook)
return mio

def get_gen_block_mesh_hook(dims, shape, centre, mat_id=0, name='block',


coors=None, verbose=True):
"""

Parameters
----------
dims : array of 2 or 3 floats
Dimensions of the block.
shape : array of 2 or 3 ints
Shape (counts of nodes in x, y, z) of the block mesh.
centre : array of 2 or 3 floats
Centre of the block.
mat_id : int, optional
The material id of all elements.
name : string
Mesh name.
verbose : bool
If True, show progress of the mesh generation.

Returns
-------
mio : UserMeshIO instance
"""
def mesh_hook(mesh, mode):
"""
Generate the 1D mesh.
"""
if mode == 'read':

mesh = gen_block_mesh(dims, shape, centre, mat_id=mat_id, name=name,


coors=coors, verbose=verbose)
return mesh

elif mode == 'write':


pass

mio = UserMeshIO(mesh_hook)
return mio

def clear_folder(clear_format, confirm=False, doit=True):


"""
Deletes files matching the format

Parameters
----------
clear_format : str
confirm : bool
(continues on next page)

1.5. Examples 141


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


doit : bool
if False do not delete anything no matter the confirmation

Returns
-------
deleted_anything :
True if there was something to delete
"""
files = glob(clear_format)
if confirm:
for file in files:
output("Will delete file {}".format(file))
doit = input("--------------\nDelete files [Y/n]? ").strip() == "Y"

if doit:
for file in files:
os.remove(file)
return bool(files)

dg/imperative_burgers_1D.py

Description
Burgers equation in 1D solved using discontinous Galerkin method
source code
#!/usr/bin/env python
"""
Burgers equation in 1D solved using discontinous Galerkin method
"""
import argparse
import sys
sys.path.append('.')
from os.path import join as pjoin

import numpy as nm

from sfepy.examples.dg.example_dg_common import \


clear_folder, get_gen_1D_mesh_hook
from script.dg_plot_1D import load_and_plot_fun

# sfepy imports
from sfepy.base.base import IndexedStruct
from sfepy.base.base import Struct, configure_output, output
from sfepy.discrete import (FieldVariable, Material, Integral, Function,
Equation, Equations, Problem)
from sfepy.discrete.conditions import InitialCondition, EssentialBC, Conditions
from sfepy.discrete.dg.fields import DGField
from sfepy.discrete.dg.limiters import MomentLimiter1D
from sfepy.discrete.fem import FEDomain
from sfepy.solvers.ls import ScipyDirect
(continues on next page)

142 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


from sfepy.solvers.nls import Newton
from sfepy.solvers.ts_dg_solvers import TVDRK3StepSolver
from sfepy.terms.terms_dg import Term

def parse_args(argv=None):
if argv is None:
argv = sys.argv

parser = argparse.ArgumentParser(
description='Solve Burgers equation and display animated results, '
'change script code to modify the problem.',
epilog='(c) 2019 T. Zitka , Man-machine Interaction at NTC UWB')
parser.add_argument('-o', '--output-dir', default='.',
help='output directory')
parser.add_argument('-p', '--plot',
action='store_true', dest='plot',
default=False, help='plot animated results')
options = parser.parse_args(argv[1:])
return options

def main(argv=None):
options = parse_args(argv=argv)

# vvvvvvvvvvvvvvvv #
approx_order = 2
# ^^^^^^^^^^^^^^^^ #

# Setup output names


outputs_folder = options.output_dir

domain_name = "domain_1D"
problem_name = "iburgers_1D"
output_folder = pjoin(outputs_folder, problem_name, str(approx_order))
output_format = "vtk"
save_timestn = 100
clear_folder(pjoin(output_folder, "*." + output_format))
configure_output({'output_screen': True,
'output_log_name':
pjoin(output_folder,
f"last_run_{problem_name}_{approx_order}.txt")})

# ------------
# | Get mesh |
# ------------
X1 = 0.
XN = 1.
n_nod = 100
n_el = n_nod - 1
mesh = get_gen_1D_mesh_hook(X1, XN, n_nod).read(None)

# -----------------------------
# | Create problem components |
(continues on next page)

1.5. Examples 143


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


# -----------------------------

integral = Integral('i', order=approx_order * 2)


domain = FEDomain(domain_name, mesh)
omega = domain.create_region('Omega', 'all')
left = domain.create_region('Gamma1',
'vertices in x == %.10f ' % X1,
'vertex')
right = domain.create_region('Gamma2',
'vertices in x == %.10f ' % XN,
'vertex')
field = DGField('dgfu', nm.float64, 'scalar', omega,
approx_order=approx_order)

u = FieldVariable('u', 'unknown', field, history=1)


v = FieldVariable('v', 'test', field, primary_var_name='u')

MassT = Term.new('dw_dot(v, u)', integral, omega, u=u, v=v)

velo = nm.array(1.0)

def adv_fun(u):
vu = velo.T * u[..., None]
return vu

def adv_fun_d(u):
v1 = velo.T * nm.ones(u.shape + (1,))
return v1

burg_velo = velo.T / nm.linalg.norm(velo)

def burg_fun(u):
vu = burg_velo * u[..., None] ** 2
return vu

def burg_fun_d(u):
v1 = 2 * burg_velo * u[..., None]
return v1

StiffT = Term.new('dw_ns_dot_grad_s(fun, fun_d, u[-1], v)',


integral, omega,
u=u, v=v,
fun=burg_fun, fun_d=burg_fun_d)

# alpha = Material('alpha', val=[.0])


# FluxT = AdvectDGFluxTerm("adv_lf_flux(a.val, v, u)", "a.val, v, u[-1]",
(continues on next page)

144 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


# integral, omega, u=u, v=v, a=a, alpha=alpha)

FluxT = Term.new('dw_dg_nonlinear_laxfrie_flux(fun, fun_d, v, u[-1])',


integral, omega,
u=u, v=v,
fun=burg_fun, fun_d=burg_fun_d)

eq = Equation('balance', MassT - StiffT + FluxT)


eqs = Equations([eq])

# ------------------------------
# | Create boundary conditions |
# ------------------------------
left_fix_u = EssentialBC('left_fix_u', left, {'u.all': 1.0})
right_fix_u = EssentialBC('right_fix_u', right, {'u.all': 0.0})

# ----------------------------
# | Create initial condition |
# ----------------------------
def ghump(x):
"""
Nice gaussian.
"""
return nm.exp(-200 * x ** 2)

def ic_wrap(x, ic=None):


return ghump(x - .3)

ic_fun = Function('ic_fun', ic_wrap)


ics = InitialCondition('ic', omega, {'u.0': ic_fun})

# ------------------
# | Create problem |
# ------------------
pb = Problem(problem_name,
equations=eqs,
conf=Struct(options={"save_times": save_timestn},
ics={}, ebcs={}, epbcs={}, lcbcs={},
materials={}),
active_only=False)
pb.setup_output(output_dir=output_folder, output_format=output_format)
pb.set_ics(Conditions([ics]))

# ------------------
# | Create limiter |
# ------------------
limiter = MomentLimiter1D

# ---------------------------
# | Set time discretization |
(continues on next page)

1.5. Examples 145


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


# ---------------------------
CFL = .2
max_velo = nm.max(nm.abs(velo))
t0 = 0
t1 = .2
dx = nm.min(mesh.cmesh.get_volumes(1))
dt = dx / max_velo * CFL / (2 * approx_order + 1)
tn = int(nm.ceil((t1 - t0) / dt))
dtdx = dt / dx

# ------------------
# | Create solver |
# ------------------
ls = ScipyDirect({})
nls_status = IndexedStruct()
nls = Newton({'is_linear': True}, lin_solver=ls, status=nls_status)

tss_conf = {'t0' : t0,


't1' : t1,
'n_step' : tn,
'limiters': {"dgfu": limiter}}

tss = TVDRK3StepSolver(tss_conf,
nls=nls, context=pb, verbose=True)

# ---------
# | Solve |
# ---------
pb.set_solver(tss)
state_end = pb.solve()

output("Solved equation \n\n\t\t u_t - div(f(u))) = 0\n")


output(f"With IC: {ic_fun.name}")
# output("and EBCs: {}".format(pb.ebcs.names))
# output("and EPBCS: {}".format(pb.epbcs.names))
output("-------------------------------------")
output(f"Approximation order is {approx_order}")
output(f"Space divided into {mesh.n_el} cells, " +
f"{len(mesh.coors)} steps, step size is {dx}")
output(f"Time divided into {tn - 1} nodes, {tn} steps, step size is {dt}")
output(f"CFL coefficient was {CFL} and " +
f"order correction {1 / (2 * approx_order + 1)}")
output(f"Courant number c = max(abs(u)) * dt/dx = {max_velo * dtdx}")
output("------------------------------------------")
output(f"Time stepping solver is {tss.name}")
output(f"Limiter used: {limiter.name}")
output("======================================")

# ----------
# | Plot 1D|
# ----------
if options.plot:
(continues on next page)

146 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


load_and_plot_fun(output_folder, domain_name,
t0, t1, min(tn, save_timestn),
ic_fun)

if __name__ == '__main__':
main()

dg/laplace_2D.py

Description
Laplace equation solved in 2d by discontinous Galerkin method

βˆ’π‘‘π‘–π‘£(π‘”π‘Ÿπ‘Žπ‘‘ 𝑝) = 0

on rectangle
p = 0 p_y = 0
[0,b]—————————–[a, b]
|
|
p_x = -a | p(x,y) | p_x = 0 p = 0 | | p = 0
|
[0,0]—————————–[a, 0] p_y = b p = 0
solution to this is

𝑝(π‘₯, 𝑦) = 1/2 * π‘₯ * *2 βˆ’ 1/2 * 𝑦 * *2 βˆ’ π‘Ž * π‘₯ + 𝑏 * 𝑦

Usage Examples

Run with simple.py script:

python simple.py examples/dg/laplace_2D.py

Results are saved to output/dg/laplace_2D folder by default as .msh files, the best way to view them is through GMSH
(http://gmsh.info/) version 4.6 or newer. Start GMSH and use File | Open menu or Crtl + O shortcut, navigate to the
output folder, select all .msh files and hit Open, all files should load as one item in Post-processing named p_cell_nodes.
GMSH is capable of rendering high order approximations in individual elements, to modify fidelity of rendering, double
click the displayed mesh, quick options menu should pop up, click on All view options.... This brings up the
Options window with View [0] selected in left column. Under the tab General ensure that Adapt visualization
grid is ticked, then you can adjust Maximum recursion depth and `Target visualization error to tune the
visualization. To see visualization elements (as opposed to mesh elements) go to Visibility tab and tick Draw
element outlines, this option is also available from quick options menu as View element outlines or under
shortcut Alt+E. In the quick options menu, you can also modify normal raise by clicking View Normal Raise to see
solution rendered as surface above the mesh. Note that for triangular meshes normal raise -1 produces expected raise
above the mesh. This is due to the opposite orientation of the reference elements in GMSH and Sfepy and might get
patched in the future.

1.5. Examples 147


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Laplace equation solved in 2d by discontinous Galerkin method

.. math:: - div(grad\,p) = 0

on rectangle
p = 0
p_y = 0
[0,b]-----------------------------[a, b]
| |
| |
p_x = -a | p(x,y) | p_x = 0
p = 0 | | p = 0
| |
[0,0]-----------------------------[a, 0]
p_y = b
p = 0

solution to this is
.. math:: p(x,y) = 1/2*x**2 - 1/2*y**2 - a*x + b*y

Usage Examples
--------------

Run with simple.py script::

python simple.py sfepy/examples/dg/laplace_2D.py

Results are saved to output/dg/laplace_2D folder by default as ``.msh`` files,


the best way to view them is through GMSH (http://gmsh.info/) version 4.6 or
newer. Start GMSH and use ``File | Open`` menu or Crtl + O shortcut, navigate to
the output folder, select all ``.msh`` files and hit Open, all files should load
as one item in Post-processing named p_cell_nodes.

GMSH is capable of rendering high order approximations in individual elements,


to modify fidelity of rendering, double click the displayed mesh, quick options
menu should pop up, click on ``All view options...``. This brings up the Options
window with ``View [0]`` selected in left column. Under the tab ``General``
ensure that ``Adapt visualization grid`` is ticked, then you can adjust
``Maximum recursion depth`` and ```Target visualization error`` to tune
the visualization. To see visualization elements (as opposed to mesh elements)
go to ``Visibility`` tab and tick ``Draw element outlines``, this option is also
available from quick options menu as ``View element outlines`` or under shortcut
``Alt+E``. In the quick options menu, you can also modify normal raise by
clicking ``View Normal Raise`` to see solution rendered as surface above the
mesh. Note that for triangular meshes normal raise -1 produces expected raise
above the mesh. This is due to the opposite orientation of the reference
elements in GMSH and Sfepy and might get patched in the future.
"""

(continues on next page)

148 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


from sfepy.examples.dg.example_dg_common import *

def define(filename_mesh=None,
approx_order=2,

adflux=None,
limit=False,

cw=100,
diffcoef=1,
diffscheme="symmetric",

cfl=None,
dt=None,
):

cfl = None
dt = None

functions = {}
def local_register_function(fun):
try:
functions.update({fun.__name__: (fun,)})

except AttributeError: # Already a sfepy Function.


fun = fun.function
functions.update({fun.__name__: (fun,)})

return fun

example_name = "laplace_2D"
dim = 2

if filename_mesh is None:
filename_mesh = get_gen_block_mesh_hook((1., 1.), (16, 16), (.5, .5))

a = 1
b = 1
c = 0

regions = {
'Omega' : 'all',
'left' : ('vertices in x == 0', 'edge'),
'right': ('vertices in x == 1', 'edge'),
'top' : ('vertices in y == 1', 'edge'),
'bottom': ('vertices in y == 0', 'edge')
}
fields = {
'f': ('real', 'scalar', 'Omega', str(approx_order) + 'd', 'DG', 'legendre') #
}

variables = {
(continues on next page)

1.5. Examples 149


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'p': ('unknown field', 'f', 0, 1),
'v': ('test field', 'f', 'p'),
}

def analytic_sol(coors, t):


x_1, x_2 = coors[..., 0], coors[..., 1]
res = 1/2*x_1**2 - 1/2*x_2**2 - a*x_1 + b*x_2 + c
return res

@local_register_function
def sol_fun(ts, coors, mode="qp", **kwargs):
t = ts.time
if mode == "qp":
return {"p": analytic_sol(coors, t)[..., None, None]}

@local_register_function
def bc_funs(ts, coors, bc, problem):
t = ts.time
x_1, x_2 = coors[..., 0], coors[..., 1]
res = nm.zeros(x_1.shape)
if bc.diff == 0:
res[:] = analytic_sol(coors, t)

elif bc.diff == 1:
res = nm.stack((x_1 - a, -x_2 + b),
axis=-2)
return res

materials = {
'D' : ({'val': [diffcoef], '.Cw': cw},),
}

dgebcs = {
'u_left' : ('left', {'p.all': "bc_funs", 'grad.p.all': "bc_funs"}),
'u_right' : ('right', {'p.all': "bc_funs", 'grad.p.all': "bc_funs"}),
'u_bottom' : ('bottom', {'p.all': "bc_funs", 'grad.p.all': "bc_funs"}),
'u_top' : ('top', {'p.all': "bc_funs", 'grad.p.all': "bc_funs"}),

integrals = {
'i': 2 * approx_order,
}

equations = {
'laplace': " dw_laplace.i.Omega(D.val, v, p) " +
diffusion_schemes_implicit[diffscheme] +
" + dw_dg_interior_penalty.i.Omega(D.val, D.Cw, v, p)" +
" = 0"
}

(continues on next page)

150 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


solver_0 = {
'name' : 'ls',
'kind' : 'ls.scipy_direct',
}

solver_1 = {
'name' : 'newton',
'kind' : 'nls.newton',

# 'i_max' : 5,
# 'eps_a' : 1e-8,
# 'eps_r' : 1.0,
# 'macheps' : 1e-16,
# 'lin_red' : 1e-2, # Linear system error < (eps_a * lin_red).
# 'ls_red' : 0.1,
# 'ls_red_warp' : 0.001,
# 'ls_on' : 0.99999,
# 'ls_min' : 1e-5,
# 'check' : 0,
# 'delta' : 1e-6,
}

options = {
'nls' : 'newton',
'ls' : 'ls',
'output_dir' : 'output/dg/' + example_name,
'output_format' : 'msh',
'file_format' : 'gmsh-dg',
# 'pre_process_hook': get_cfl_setup(cfl)
}
return locals()

globals().update(define())

diffusion

diffusion/cube.py

Description
Laplace equation (e.g. temperature distribution) on a cube geometry with different boundary condition values on the
cube sides. This example was used to create the SfePy logo.
Find 𝑇 such that:
∫︁
π‘βˆ‡π‘  Β· βˆ‡π‘‡ = 0 , βˆ€π‘  .
Ξ©

1.5. Examples 151


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Laplace equation (e.g. temperature distribution) on a cube geometry with
different boundary condition values on the cube sides. This example was
used to create the SfePy logo.

Find :math:`T` such that:

.. math::
\int_{\Omega} c \nabla s \cdot \nabla T
= 0
\;, \quad \forall s \;.
"""
from __future__ import absolute_import
from sfepy import data_dir

#filename_mesh = data_dir + '/meshes/3d/cube_big_tetra.mesh'


filename_mesh = data_dir + '/meshes/3d/cube_medium_hexa.mesh'

############# Laplace.

material_1 = {
(continues on next page)

152 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'name' : 'coef',
'values' : {'val' : 1.0},
}

field_1 = {
'name' : 'temperature',
'dtype' : 'real',
'shape' : (1,),
'region' : 'Omega',
'approx_order' : 1,
}

if filename_mesh.find('cube_medium_hexa.mesh') >= 0:
region_1000 = {
'name' : 'Omega',
'select' : 'cells of group 0',
}
integral_1 = {
'name' : 'i',
'order' : 1,
}
solver_0 = {
'name' : 'ls',
'kind' : 'ls.scipy_direct',
}

elif filename_mesh.find('cube_big_tetra.mesh') >= 0:


region_1000 = {
'name' : 'Omega',
'select' : 'cells of group 6',
}
integral_1 = {
'name' : 'i',
'quadrature' : 'custom',
'vals' : [[1./3., 1./3., 1./3.]],
'weights' : [0.5]
}
solver_0 = {
'name' : 'ls',
'kind' : 'ls.scipy_iterative',

'method' : 'cg',
'i_max' : 1000,
'eps_r' : 1e-12,
}

variable_1 = {
'name' : 'T',
'kind' : 'unknown field',
'field' : 'temperature',
'order' : 0, # order in the global vector of unknowns
}
(continues on next page)

1.5. Examples 153


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

variable_2 = {
'name' : 's',
'kind' : 'test field',
'field' : 'temperature',
'dual' : 'T',
}

region_0 = {
'name' : 'Surface',
'select' : 'vertices of surface',
'kind' : 'facet',
}
region_1 = {
'name' : 'Bottom',
'select' : 'vertices in (z < -0.4999999)',
'kind' : 'facet',
}
region_2 = {
'name' : 'Top',
'select' : 'vertices in (z > 0.4999999)',
'kind' : 'facet',
}
region_03 = {
'name' : 'Left',
'select' : 'vertices in (x < -0.4999999)',
'kind' : 'facet',
}

ebc_1 = {
'name' : 'T0',
'region' : 'Surface',
'dofs' : {'T.0' : -3.0},
}
ebc_4 = {
'name' : 'T1',
'region' : 'Top',
'dofs' : {'T.0' : 1.0},
}
ebc_3 = {
'name' : 'T2',
'region' : 'Bottom',
'dofs' : {'T.0' : -1.0},
}
ebc_2 = {
'name' : 'T3',
'region' : 'Left',
'dofs' : {'T.0' : 2.0},
}

equations = {
'nice_equation' : """dw_laplace.i.Omega( coef.val, s, T ) = 0""",
(continues on next page)

154 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


}

solver_1 = {
'name' : 'newton',
'kind' : 'nls.newton',

'i_max' : 1,
'eps_a' : 1e-10,
'eps_r' : 1.0,
'macheps' : 1e-16,
'lin_red' : 1e-2, # Linear system error < (eps_a * lin_red).
'ls_red' : 0.1,
'ls_red_warp' : 0.001,
'ls_on' : 1.1,
'ls_min' : 1e-5,
'check' : 0,
'delta' : 1e-6,
}

diffusion/darcy_flow_multicomp.py

Description
Each of the two equations describes a flow in one compartment of a porous medium. The equations are based on the
Darcy flow and the i-th compartment is defined in Ω𝑖 .
∫︁ ∫︁ βˆ‘οΈ ∫︁
𝐾 𝑖 βˆ‡π‘π‘– Β· βˆ‡π‘ž 𝑖 + Β― π‘˜ 𝑝𝑖 βˆ’ 𝑝𝑗 π‘ž 𝑖 = 𝑓 𝑖 π‘žπ‘– ,
(οΈ€ )οΈ€
𝐺𝛼
Ω𝑖 Ω𝑖 𝑗 Ω𝑖

βˆ€π‘ž 𝑖 ∈ 𝑄𝑖 , 𝑖, 𝑗 = 1, 2 and 𝑖 ΜΈ= 𝑗,
where 𝐾 is the local permeability of the i-th compartment, 𝐺𝛼
𝑖 Β― π‘˜ = 𝐺𝑖 is the perfusion coefficient related to the
𝑗
compartments 𝑖 and 𝑗, 𝑓 𝑖 are sources or sinks which represent the external flow into the i-th compartment and 𝑝𝑖 is the
pressure in the i-th compartment.

1.5. Examples 155


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Each of the two equations describes a flow in one compartment of a porous
medium. The equations are based on the Darcy flow and the i-th compartment is
defined in :math:`\Omega_{i}`.

.. math::
\int_{\Omega_{i}} K^{i} \nabla p^{i} \cdot \nabla q^{i}+\int_{\Omega_{i}}
\sum_{j} \bar{G}\alpha_{k} \left( p^{i}-p^{j} \right)q^{i}
= \int_{\Omega_{i}} f^{i} q^{i},
.. math::
\forall q^{i} \in Q^{i}, \quad i,j=1,2 \quad \mbox{and} \quad i\neq j,

where :math:`K^{i}` is the local permeability of the i-th compartment,


:math:`\bar{G}\alpha_{k} = G^{i}_{j}` is the perfusion coefficient
related to the compartments :math:`i` and :math:`j`, :math:`f^i` are
sources or sinks which represent the external flow into the i-th
compartment and :math:`p^{i}` is the pressure in the i-th compartment.
"""

from __future__ import absolute_import


from sfepy.base.base import Struct
(continues on next page)

156 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


import numpy as nm
from sfepy import data_dir

filename_mesh = data_dir + '/meshes/3d/cube_medium_hexa.mesh'


G_bar = 2.0
alpha1 = 1.3
alpha2 = 1.0

materials = {
'mat': ('mat_fun')
}

regions = {
'Omega': 'cells of group 0',
'Sigma_1': ('vertex 0', 'vertex'),
'Omega1': ('copy r.Omega', 'cell', 'Omega'),
'Omega2': ('copy r.Omega', 'cell', 'Omega'),
'Source': 'cell 24',
'Sink': 'cell 1',
}

fields = {
'pressure':('real', 1, 'Omega', 1)
}

variables = {
'p1': ('unknown field', 'pressure'),
'q1': ('test field', 'pressure', 'p1'),
'p2': ('unknown field', 'pressure'),
'q2': ('test field', 'pressure', 'p2'),
}

ebcs = {
'P1': ('Sigma_1', {'p1.0' : 0.0}),
}

equations = {
'komp1': """dw_diffusion.5.Omega1(mat.K, q1, p1)
+ dw_dot.5.Omega1(mat.G_alfa, q1, p1)
- dw_dot.5.Omega1(mat.G_alfa, q1, p2)
= dw_integrate.5.Source(mat.f_1, q1)""",

'komp2': """dw_diffusion.5.Omega2(mat.K, q2, p2)


+ dw_dot.5.Omega2(mat.G_alfa, q2, p2)
- dw_dot.5.Omega2(mat.G_alfa, q2, p1)
= dw_integrate.5.Sink(mat.f_2, q2)"""
}

solvers = {
'ls': ('ls.scipy_direct', {}),
'newton': ('nls.newton',
{'i_max' : 1,
(continues on next page)

1.5. Examples 157


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'eps_a' : 1e-6,
'eps_r' : 1.0,
})
}

def mat_fun(ts, coors, mode=None, **kwargs):


if mode == 'qp':
nqp, dim = coors.shape
alpha = nm.zeros((nqp,1,1), dtype=nm.float64)
alpha[0:nqp // 2,...] = alpha1
alpha[nqp // 2:,...] = alpha2
K = nm.eye(dim, dtype=nm.float64)
K2 = nm.tile(K, (nqp,1,1))
out = {
'K' : K2,
'f_1': 20.0 * nm.ones((nqp,1,1), dtype=nm.float64),
'f_2': -20.0 * nm.ones((nqp,1,1), dtype=nm.float64),
'G_alfa': G_bar * alpha,
}

return out

functions = {
'mat_fun': (mat_fun,),
}

options = {
'post_process_hook': 'postproc',
}

def postproc(out, pb, state, extend=False):


alpha = pb.evaluate('ev_integrate_mat.5.Omega(mat.G_alfa, p1)',
mode='el_avg')
out['alpha'] = Struct(name='output_data',
mode='cell',
data=alpha.reshape(alpha.shape[0], 1, 1, 1),
dofs=None)
return out

diffusion/laplace_1d.py

Description
Laplace equation in 1D with a variable coefficient.
Because the mesh is trivial in 1D, it is generated by mesh_hook(), and registered using UserMeshIO.
Find 𝑑 such that:
∫︁
d𝑠 d𝑑
𝑐(π‘₯) =0, βˆ€π‘  ,
Ξ© dπ‘₯ dπ‘₯

where the coefficient 𝑐(π‘₯) = 0.1 + sin(2πœ‹π‘₯)2 is computed in get_coef().

158 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

View the results using:


$ ./postproc.py -b -d't,plot_warp_scalar,rel_scaling=1' --wireframe --view=-90,90,1.5,0,
Λ“β†’0,0 --roll=0 laplace_1d.vtk

source code
r"""
Laplace equation in 1D with a variable coefficient.

Because the mesh is trivial in 1D, it is generated by :func:`mesh_hook()`, and


registered using :class:`UserMeshIO <sfepy.discrete.fem.meshio.UserMeshIO>`.

Find :math:`t` such that:

.. math::
\int_{\Omega} c(x) \tdiff{s}{x} \tdiff{t}{x}
= 0
\;, \quad \forall s \;,

where the coefficient :math:`c(x) = 0.1 + \sin(2 \pi x)^2` is computed in


:func:`get_coef()`.

View the results using::


(continues on next page)

1.5. Examples 159


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

$ ./postproc.py -b -d't,plot_warp_scalar,rel_scaling=1' --wireframe --view=-90,90,1.5,0,


Λ“β†’0,0 --roll=0 laplace_1d.vtk
"""
from __future__ import absolute_import
import numpy as nm
from sfepy.discrete.fem import Mesh
from sfepy.discrete.fem.meshio import UserMeshIO

def mesh_hook(mesh, mode):


"""
Generate the 1D mesh.
"""
if mode == 'read':
n_nod = 101

coors = nm.linspace(0.0, 1.0, n_nod).reshape((n_nod, 1))


conn = nm.arange(n_nod, dtype=nm.int32).repeat(2)[1:-1].reshape((-1, 2))
mat_ids = nm.zeros(n_nod - 1, dtype=nm.int32)
descs = ['1_2']

mesh = Mesh.from_data('laplace_1d', coors, None,


[conn], [mat_ids], descs)
return mesh

elif mode == 'write':


pass

def get_coef(ts, coors, mode=None, **kwargs):


if mode == 'qp':
x = coors[:, 0]

val = 0.1 + nm.sin(2 * nm.pi * x)**2


val.shape = (coors.shape[0], 1, 1)

return {'val' : val}

filename_mesh = UserMeshIO(mesh_hook)

materials = {
'coef' : 'get_coef',
}

functions = {
'get_coef' : (get_coef,),
}

regions = {
'Omega' : 'all',
'Gamma_Left' : ('vertices in (x < 0.00001)', 'facet'),
'Gamma_Right' : ('vertices in (x > 0.99999)', 'facet'),
}
(continues on next page)

160 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

fields = {
'temperature' : ('real', 1, 'Omega', 1),
}

variables = {
't' : ('unknown field', 'temperature', 0),
's' : ('test field', 'temperature', 't'),
}

ebcs = {
't1' : ('Gamma_Left', {'t.0' : 0.3}),
't2' : ('Gamma_Right', {'t.0' : -0.3}),
}

integrals = {
'i' : 2,
}

equations = {
'Temperature' : """dw_laplace.i.Omega(coef.val, s, t) = 0"""
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
}),
}

options = {
'nls' : 'newton',
'ls' : 'ls',
}

diffusion/laplace_coupling_lcbcs.py

Description
Two Laplace equations with multiple linear combination constraints.
The two equations are coupled by a periodic-like boundary condition constraint with a shift, given as a non-
homogeneous linear combination boundary condition.

1.5. Examples 161


SfePy Documentation, Release version: 2022.1+git.f9873484

Find 𝑒 such that:


∫︁
βˆ‡π‘£1 Β· βˆ‡π‘’1 = 0 , βˆ€π‘£1 ,
Ξ©1
∫︁
βˆ‡π‘£2 Β· βˆ‡π‘’2 = 0 , βˆ€π‘£2 ,
Ξ©2
𝑒1 = 0 on Ξ“π‘π‘œπ‘‘π‘‘π‘œπ‘š ,
𝑒2 = 1 on Ξ“π‘‘π‘œπ‘ ,
𝑒1 (π‘₯) = 𝑒2 (π‘₯) + π‘Ž(π‘₯) for π‘₯ ∈ Ξ“ = Ω̄1 ∩ Ω̄2
𝑒1 (π‘₯) = 𝑒1 (𝑦) + 𝑏(𝑦) for π‘₯ ∈ Γ𝑙𝑒𝑓 𝑑 , 𝑦 ∈ Ξ“π‘Ÿπ‘–π‘”β„Žπ‘‘ , 𝑦 = 𝑃 (π‘₯) ,
𝑒1 = 𝑐11 in β„¦π‘š11 βŠ‚ Ω1 ,
𝑒1 = 𝑐12 in β„¦π‘š12 βŠ‚ Ω1 ,
𝑒2 = 𝑐2 in β„¦π‘š2 βŠ‚ Ω2 ,

where π‘Ž(π‘₯), 𝑏(𝑦) are given functions (shifts), 𝑃 is the periodic coordinate mapping and 𝑐11 , 𝑐12 and 𝑐2 are unknown
constant values - the unknown DOFs in β„¦π‘š11 , β„¦π‘š12 and β„¦π‘š2 are replaced by the integral mean values.
View the results using:

$ ./postproc.py square_quad.vtk -b --wireframe -d'u1,plot_warp_scalar,rel_scaling=1:u2,


Λ“β†’plot_warp_scalar,rel_scaling=1'

source code

162 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

r"""
Two Laplace equations with multiple linear combination constraints.

The two equations are coupled by a periodic-like boundary condition constraint


with a shift, given as a non-homogeneous linear combination boundary condition.

Find :math:`u` such that:

.. math::
\int_{\Omega_1} \nabla v_1 \cdot \nabla u_1
= 0
\;, \quad \forall v_1 \;,

\int_{\Omega_2} \nabla v_2 \cdot \nabla u_2


= 0
\;, \quad \forall v_2 \;,

u_1 = 0 \mbox{ on } \Gamma_{bottom} \;,

u_2 = 1 \mbox{ on } \Gamma_{top} \;,

u_1(\ul{x}) = u_2(\ul{x}) + a(\ul{x}) \mbox{ for }


\ul{x} \in \Gamma = \bar\Omega_1 \cap \bar\Omega_2

u_1(\ul{x}) = u_1(\ul{y}) + b(\ul{y}) \mbox{ for }


\ul{x} \in \Gamma_{left}, \ul{y} \in \Gamma_{right}, \ul{y} = P(\ul{x}) \;,

u_1 = c_{11} \mbox{ in } \Omega_{m11} \subset \Omega_1 \;,

u_1 = c_{12} \mbox{ in } \Omega_{m12} \subset \Omega_1 \;,

u_2 = c_2 \mbox{ in } \Omega_{m2} \subset \Omega_2 \;,

where :math:`a(\ul{x})`, :math:`b(\ul{y})` are given functions (shifts),


:math:`P` is the periodic coordinate mapping and :math:`c_{11}`, :math:`c_{12}`
and :math:`c_2` are unknown constant values - the unknown DOFs in
:math:`\Omega_{m11}`, :math:`\Omega_{m12}` and :math:`\Omega_{m2}` are replaced
by the integral mean values.

View the results using::

$ ./postproc.py square_quad.vtk -b --wireframe -d'u1,plot_warp_scalar,rel_scaling=1:u2,


Λ“β†’plot_warp_scalar,rel_scaling=1'
"""
from __future__ import absolute_import
import numpy as nm

import sfepy.discrete.fem.periodic as per


from sfepy import data_dir

filename_mesh = data_dir + '/meshes/2d/square_quad.mesh'

options = {
(continues on next page)

1.5. Examples 163


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'nls' : 'newton',
'ls' : 'ls',
}

def get_shift1(ts, coors, region):


val = 0.1 * coors[:, 0]

return val

def get_shift2(ts, coors, region):


val = nm.empty_like(coors[:, 1])
val.fill(0.3)

return val

functions = {
'get_shift1' : (get_shift1,),
'get_shift2' : (get_shift2,),
'match_y_line' : (per.match_y_line,),
'match_x_line' : (per.match_x_line,),
}

fields = {
'scalar1': ('real', 1, 'Omega1', 1),
'scalar2': ('real', 1, 'Omega2', 1),
}

materials = {
}

variables = {
'u1' : ('unknown field', 'scalar1', 0),
'v1' : ('test field', 'scalar1', 'u1'),
'u2' : ('unknown field', 'scalar2', 1),
'v2' : ('test field', 'scalar2', 'u2'),
}

regions = {
'Omega1' : 'cells of group 1',
'Omega2' : 'cells of group 2',
'Omega_m1' : 'r.Omega1 -v (r.Gamma +s vertices of surface)',
'Omega_m11' : 'r.Omega_m1 *v vertices in (x < 0)',
'Omega_m12' : 'r.Omega_m1 *v vertices in (x > 0)',
'Omega_m2' : 'r.Omega2 -v (r.Gamma +s vertices of surface)',
'Left' : ('vertices in (x < -0.499)', 'facet'),
'Right' : ('vertices in (x > 0.499)', 'facet'),
'Bottom' : ('vertices in ((y < -0.499))', 'facet'),
'Top' : ('vertices in ((y > 0.499))', 'facet'),
'Gamma' : ('r.Omega1 *v r.Omega2 -v vertices of surface', 'facet'),
'Gamma1' : ('copy r.Gamma', 'facet', 'Omega1'),
'Gamma2' : ('copy r.Gamma', 'facet', 'Omega2'),
}
(continues on next page)

164 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

ebcs = {
'fix1' : ('Top', {'u2.all' : 1.0}),
'fix2' : ('Bottom', {'u1.all' : 0.0}),
}

lcbcs = {
'shifted1' : (('Gamma1', 'Gamma2'),
{'u1.all' : 'u2.all'},
'match_x_line', 'shifted_periodic',
'get_shift1'),
'shifted2' : (('Left', 'Right'),
{'u1.all' : 'u1.all'},
'match_y_line', 'shifted_periodic',
'get_shift2'),
'mean11' : ('Omega_m11', {'u1.all' : None}, None, 'integral_mean_value'),
'mean12' : ('Omega_m12', {'u1.all' : None}, None, 'integral_mean_value'),
'mean2' : ('Omega_m2', {'u2.all' : None}, None, 'integral_mean_value'),
}

integrals = {
'i1' : 2,
}

equations = {
'eq1' : """
dw_laplace.i1.Omega1(v1, u1) = 0
""",
'eq2' : """
dw_laplace.i1.Omega2(v2, u2) = 0
""",
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
}),
}

1.5. Examples 165


SfePy Documentation, Release version: 2022.1+git.f9873484

diffusion/laplace_fluid_2d.py

Description
A Laplace equation that models the flow of β€œdry water” around an obstacle shaped like a Citroen CX.

Description

As discussed e.g. in the Feynman lectures Section 12-5 of Volume 2 (https://www.feynmanlectures.caltech.edu/II_12.


html#Ch12-S5), the flow of an irrotational and incompressible fluid can be modelled with a potential 𝑣 = π‘”π‘Ÿπ‘Žπ‘‘(πœ‘)
that obeys

βˆ‡ Β· βˆ‡πœ‘ = βˆ†πœ‘ = 0

The weak formulation for this problem is to find πœ‘ such that:


∫︁ ∫︁ ∫︁ ∫︁ ∫︁
βˆ‡πœ“ Β· βˆ‡πœ‘ = 𝑣0 Β· 𝑛 πœ“ + 𝑣0 Β· 𝑛 πœ“ + 𝑣0 Β· 𝑛 πœ“ + 𝑣0 Β· 𝑛 πœ“ , βˆ€πœ“ ,
Ξ© Γ𝑙𝑒𝑓 𝑑 Ξ“π‘Ÿπ‘–π‘”β„Žπ‘‘ Ξ“π‘‘π‘œπ‘ Ξ“π‘π‘œπ‘‘π‘‘π‘œπ‘š

where 𝑣 0 is the 2D vector defining the far field velocity that generates the incompressible flow.
Since the value of the potential is defined up to a constant value, a Dirichlet boundary condition is set at a single vertex
to avoid having a singular matrix.

Usage examples

This example can be run with the simple.py script with the following:

python3 simple.py examples/diffusion/laplace_fluid_2d.py


python3 resview.py citroen.vtk -f phi:p0 phi:t50:p0 --2d-view

166 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

Generating the mesh

The mesh can be generated with:

gmsh -2 -f msh22 meshes/2d/citroen.geo -o meshes/2d/citroen.msh


python3 script/convert_mesh.py --2d meshes/2d/citroen.msh meshes/2d/citroen.h5

source code

r"""
A Laplace equation that models the flow of "dry water" around an obstacle
shaped like a Citroen CX.

Description
-----------

As discussed e.g. in the Feynman lectures Section 12-5 of Volume 2


(https://www.feynmanlectures.caltech.edu/II_12.html#Ch12-S5),
the flow of an irrotational and incompressible fluid can be modelled with a
potential :math:`\ul{v} = \ul{grad}(\phi)` that obeys

.. math::
(continues on next page)

1.5. Examples 167


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


\nabla \cdot \ul{\nabla}\,\phi = \Delta\,\phi = 0

The weak formulation for this problem is to find :math:`\phi` such that:

.. math::
\int_{\Omega} \nabla \psi \cdot \nabla \phi
= \int_{\Gamma_{left}} \ul{v}_0 \cdot n \, \psi
+ \int_{\Gamma_{right}} \ul{v}_0 \cdot n \, \psi
+ \int_{\Gamma_{top}} \ul{v}_0 \cdot n \,\psi
+ \int_{\Gamma_{bottom}} \ul{v}_0 \cdot n \, \psi
\;, \quad \forall \psi \;,

where :math:`\ul{v}_0` is the 2D vector defining the far field velocity that
generates the incompressible flow.

Since the value of the potential is defined up to a constant value, a Dirichlet


boundary condition is set at a single vertex to avoid having a singular matrix.

Usage examples
--------------

This example can be run with the ``simple.py`` script with the following::

python3 simple.py sfepy/examples/diffusion/laplace_fluid_2d.py


python3 resview.py citroen.vtk -f phi:p0 phi:t50:p0 --2d-view

Generating the mesh


-------------------

The mesh can be generated with::

gmsh -2 -f msh22 meshes/2d/citroen.geo -o meshes/2d/citroen.msh


python3 script/convert_mesh.py --2d meshes/2d/citroen.msh meshes/2d/citroen.h5

"""
import numpy as nm
from sfepy import data_dir

filename_mesh = data_dir + '/meshes/2d/citroen.h5'

v0 = nm.array([1, 0.25])

materials = {
'm': ({'v0': v0.reshape(-1, 1)},),
}

regions = {
'Omega': 'all',
'Gamma_Left': ('vertices in (x < 0.1)', 'facet'),
'Gamma_Right': ('vertices in (x > 1919.9)', 'facet'),
'Gamma_Top': ('vertices in (y > 917.9)', 'facet'),
(continues on next page)

168 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'Gamma_Bottom': ('vertices in (y < 0.1)', 'facet'),
'Vertex': ('vertex in r.Gamma_Left', 'vertex'),
}

fields = {
'u': ('real', 1, 'Omega', 1),
}

variables = {
'phi': ('unknown field', 'u', 0),
'psi': ('test field', 'u', 'phi'),
}

# these EBCS prevent the matrix from being singular, see description
ebcs = {
'fix': ('Vertex', {'phi.0': 0.0}),
}

integrals = {
'i': 2,
}

equations = {
'Laplace equation':
"""dw_laplace.i.Omega( psi, phi )
= dw_surface_ndot.i.Gamma_Left( m.v0, psi )
+ dw_surface_ndot.i.Gamma_Right( m.v0, psi )
+ dw_surface_ndot.i.Gamma_Top( m.v0, psi )
+ dw_surface_ndot.i.Gamma_Bottom( m.v0, psi )"""
}

solvers = {
'ls': ('ls.scipy_direct', {}),
'newton': ('nls.newton', {
'i_max': 1,
'eps_a': 1e-10,
}),
}

diffusion/laplace_iga_interactive.py

Description
Laplace equation with Dirichlet boundary conditions solved in a single patch NURBS domain using the isogeometric
analysis (IGA) approach, using commands for interactive use.
This script allows the creation of a customisable NURBS surface using igakit built-in CAD routines, which is then
saved in custom HDF5-based files with .iga extension.

1.5. Examples 169


SfePy Documentation, Release version: 2022.1+git.f9873484

Notes

The create_patch function creates a NURBS-patch of the area between two coplanar nested circles using igakit
CAD built-in routines. The created patch is not connected in the orthoradial direction. This is a problem when the
disconnected boundary is not perpendicular to the line connecting the two centres of the circles, as the solution then
exhibits a discontinuity along this line. A workaround for this issue is to enforce perpendicularity by changing the start
angle in function igakit.cad.circle (see the code down below for the actual trick). The discontinuity disappears.

Usage Examples

Default options, storing results in this file’s parent directory:


$ python3 examples/diffusion/laplace_iga_interactive.py

Command line options for tweaking the geometry of the NURBS-patch & more:
$ python3 examples/diffusion/laplace_iga_interactive.py --R1=0.7 --C2=0.1,0.1 --viewpatch

View the results using:


$ python3 postproc.py concentric_circles.vtk

source code
#!/usr/bin/env python
r"""
Laplace equation with Dirichlet boundary conditions solved in a single patch
NURBS domain using the isogeometric analysis (IGA) approach, using commands
for interactive use.

This script allows the creation of a customisable NURBS surface using igakit
built-in CAD routines, which is then saved in custom HDF5-based files with
.iga extension.

Notes
-----

The ``create_patch`` function creates a NURBS-patch of the area between two


coplanar nested circles using igakit CAD built-in routines. The created patch
is not connected in the orthoradial direction. This is a problem when the
disconnected boundary is not perpendicular to the line connecting the two
centres of the circles, as the solution then exhibits a discontinuity along
this line. A workaround for this issue is to enforce perpendicularity by
changing the start angle in function ``igakit.cad.circle`` (see the code down
below for the actual trick). The discontinuity disappears.

Usage Examples
--------------

Default options, storing results in this file's parent directory::

$ python3 sfepy/examples/diffusion/laplace_iga_interactive.py

(continues on next page)

170 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


Command line options for tweaking the geometry of the NURBS-patch & more::

$ python3 sfepy/examples/diffusion/laplace_iga_interactive.py --R1=0.7 --C2=0.1,0.1 --


Λ“β†’viewpatch

View the results using::

$ python3 postproc.py concentric_circles.vtk


"""

from argparse import RawDescriptionHelpFormatter, ArgumentParser

import os
import sys
sys.path.append('.')

import numpy as nm
from sfepy import data_dir
from sfepy.base.ioutils import ensure_path
from sfepy.base.base import IndexedStruct
from sfepy.discrete import (FieldVariable, Integral, Equation,Equations,
Problem)
from sfepy.discrete.iga.domain import IGDomain
from sfepy.discrete.common.fields import Field
from sfepy.terms import Term
from sfepy.discrete.conditions import Conditions, EssentialBC
from sfepy.solvers.ls import ScipyDirect
from sfepy.solvers.nls import Newton

def create_patch(R1, R2, C1, C2, order=2, viewpatch=False):


"""
Create a single 2d NURBS-patch of the area between two coplanar nested
circles using igakit.

Parameters
----------
R1 : float
Radius of the inner circle.
R2 : float
Radius of the outer circle.
C1 : list of two floats
Coordinates of the center of the inner circle given as [x1, y1].
C2 : list of two floats
Coordinates of the center of the outer circle given as [x2, y2].
order : int, optional
Degree of the NURBS basis functions. The default is 2.
viewpatch : bool, optional
When set to True, display the NURBS patch. The default is False.

Returns
-------
None.
(continues on next page)

1.5. Examples 171


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

"""

from sfepy.discrete.iga.domain_generators import create_from_igakit


import sfepy.discrete.iga.io as io
from igakit.cad import circle, ruled
from igakit.plot import plt as iplt
from numpy import pi

# Assert the inner circle is inside the outer one


inter_centers = nm.sqrt((C2[0]-C1[0])**2 + (C2[1]-C1[1])**2)
assert R2>R1, "Outer circle should have a larger radius than the inner one"
assert inter_centers<R2-R1, "Circles are not nested"

# Geometry Creation
centers_direction = [C2[0]-C1[0], C2[1]-C1[1]]
if centers_direction[0]==0 and centers_direction[1]==0:
start_angle = 0.0
else:
start_angle = nm.arctan2(centers_direction[1], centers_direction[0])
c1 = circle(radius=R1, center=C1, angle=(start_angle, start_angle + 2*pi))
c2 = circle(radius=R2, center=C2, angle=(start_angle, start_angle + 2*pi))
srf = ruled(c1,c2).transpose() # make the radial direction first

# Refinement
insert_U = insert_uniformly(srf.knots[0], 6)
insert_V = insert_uniformly(srf.knots[1], 6)
srf.refine(0, insert_U).refine(1, insert_V)

# Setting the NURBS-surface degree


srf.elevate(0, order-srf.degree[0] if order-srf.degree[0] > 0 else 0)
srf.elevate(1, order-srf.degree[1] if order-srf.degree[1] > 0 else 0)

# Sfepy .iga file creation


nurbs, bmesh, regions = create_from_igakit(srf, verbose=True)

# Save .iga file in sfepy/meshes/iga


filename_domain = data_dir + '/meshes/iga/concentric_circles.iga'
io.write_iga_data(filename_domain, None, nurbs.knots, nurbs.degrees,
nurbs.cps, nurbs.weights, nurbs.cs, nurbs.conn,
bmesh.cps, bmesh.weights, bmesh.conn, regions)

if viewpatch:
try:
iplt.use('mayavi')
iplt.figure()
iplt.plot(srf)
iplt.show()
except ImportError:
iplt.use('matplotlib')
iplt.figure()
iplt.plot(srf)
(continues on next page)

172 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


iplt.show()

def insert_uniformly(U, n):


"""
Find knots to uniformly add to U.
[Code from igakit/demo/venturi.py file]

Given a knot vector U and the number of uniform spans desired,


find the knots which need to be inserted.

Parameters
----------
U : numpy.ndarray
Original knot vector for a C^p-1 space.
n : int
Target number of uniformly-spaced knot spans.

Returns
-------
Knots to be inserted into U
"""
U0 = U
dU=(U.max()-U.min())/float(n) # target dU in knot vector
idone=0
while idone == 0:
# Add knots in middle of spans which are too large
Uadd=[]
for i in range(len(U)-1):
if U[i+1]-U[i] > dU:
Uadd.append(0.5*(U[i+1]+U[i]))
# Now we add these knots (once only, assumes C^(p-1))
if len(Uadd) > 0:
U = nm.sort(nm.concatenate([U,nm.asarray(Uadd)]))
else:
idone=1
# And now a little Laplacian smoothing
for num_iterations in range(5):
for i in range(len(U)-2):
if abs(U0[U0.searchsorted(U[i+1])]-U[i+1]) > 1.0e-14:
U[i+1] = 0.5*(U[i]+U[i+2])
return nm.setdiff1d(U,U0)

helps = {
'output_dir' :
'output directory',
'R1' :
'Inner circle radius [default: %(default)s]',
'R2' :
'Outer circle radius [default: %(default)s]',
'C1' :
'centre of the inner circle [default: %(default)s]',
'C2' :
(continues on next page)

1.5. Examples 173


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'centre of the outer circle [default: %(default)s]',
'order' :
'field approximation order [default: %(default)s]',
'viewpatch' :
'generate a plot of the NURBS-patch',
}

def main():
parser = ArgumentParser(description=__doc__.rstrip(),
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('-o', '--output-dir', default='.',
help=helps['output_dir'])
parser.add_argument('--R1', metavar='R1',
action='store', dest='R1',
default='0.5', help=helps['R1'])
parser.add_argument('--R2', metavar='R2',
action='store', dest='R2',
default='1.0', help=helps['R2'])
parser.add_argument('--C1', metavar='C1',
action='store', dest='C1',
default='0.0,0.0', help=helps['C1'])
parser.add_argument('--C2', metavar='C2',
action='store', dest='C2',
default='0.0,0.0', help=helps['C2'])
parser.add_argument('--order', metavar='int', type=int,
action='store', dest='order',
default=2, help=helps['order'])
parser.add_argument('-v', '--viewpatch',
action='store_true', dest='viewpatch',
default=False, help=helps['viewpatch'])
options = parser.parse_args()

# Creation of the NURBS-patch with igakit


R1 = eval(options.R1)
R2 = eval(options.R2)
C1 = list(eval(options.C1))
C2 = list(eval(options.C2))
order = options.order
viewpatch = options.viewpatch
create_patch(R1, R2, C1, C2, order=order, viewpatch=viewpatch)

# Setting a Domain instance


filename_domain = data_dir + '/meshes/iga/concentric_circles.iga'
domain = IGDomain.from_file(filename_domain)

# Sub-domains
omega = domain.create_region('Omega', 'all')
Gamma_out = domain.create_region('Gamma_out', 'vertices of set xi01',
kind='facet')
Gamma_in = domain.create_region('Gamma_in', 'vertices of set xi00',
kind='facet')

(continues on next page)

174 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


# Field (featuring order elevation)
order_increase = order - domain.nurbs.degrees[0]
order_increase *= int(order_increase>0)
field = Field.from_args('fu', nm.float64, 'scalar', omega,
approx_order='iga', space='H1',
poly_space_base='iga')

# Variables
u = FieldVariable('u', 'unknown', field) # unknown function
v = FieldVariable('v', 'test', field, primary_var_name='u') # test function

# Integral
integral = Integral('i', order=2*field.approx_order)

# Term
t = Term.new('dw_laplace( v, u )', integral, omega, v=v, u=u)

# Equation
eq = Equation('laplace', t)
eqs = Equations([eq])

# Boundary Conditions
u_in = EssentialBC('u_in', Gamma_in, {'u.all' : 7.0})
u_out = EssentialBC('u_out', Gamma_out, {'u.all' : 3.0})

# solvers
ls = ScipyDirect({})
nls_status = IndexedStruct()
nls = Newton({}, lin_solver=ls, status=nls_status)

# problem instance
pb = Problem('potential', equations=eqs, active_only=True)

# Set boundary conditions


pb.set_bcs(ebcs=Conditions([u_in, u_out]))

# solving
pb.set_solver(nls)
status = IndexedStruct()
state = pb.solve(status=status, save_results=True, verbose=True)

# Saving the results to a classic VTK file


filename = os.path.join(options.output_dir, 'concentric_circles.vtk')
ensure_path(filename)
pb.save_state(filename, state)

if __name__ == '__main__':
main()

1.5. Examples 175


SfePy Documentation, Release version: 2022.1+git.f9873484

diffusion/laplace_refine_interactive.py

Description
Example of solving Laplace’s equation on a block domain refined with level 1 hanging nodes.
The domain is progressively refined towards the edge/face of the block, where Dirichlet boundary conditions are pre-
scribed by an oscillating function.
Find 𝑒 such that:
∫︁
βˆ‡π‘£ Β· βˆ‡π‘’ = 0 , βˆ€π‘  .
Ξ©

Notes

The implementation of the mesh refinement with level 1 hanging nodes is a proof-of-concept code with many unresolved
issues. The main problem is the fact that a user needs to input the cells to refine at each level, while taking care of the
following constraints:
β€’ the level 1 hanging nodes constraint: a cell that has a less-refined neighbour cannot be refined;
β€’ the implementation constraint: a cell with a refined neighbour cannot be refined.
The hanging nodes are treated by a basis transformation/DOF substitution, which has to be applied explicitly by the
user:
β€’ call field.substitute_dofs(subs) before assembling and solving;
β€’ then call field.restore_dofs() before saving results.

Usage Examples

Default options, 2D, storing results in β€˜output’ directory:

$ python examples/diffusion/laplace_refine_interactive.py output


$ python postproc.py output/hanging.vtk --wireframe -b -d'u,plot_warp_scalar'

Default options, 3D, storing results in β€˜output’ directory:

$ python examples/diffusion/laplace_refine_interactive.py -3 output


$ python postproc.py output/hanging.vtk --wireframe -b --3d

Finer initial domain, 2D, storing results in β€˜output’ directory:

$ python examples/diffusion/laplace_refine_interactive.py --shape=11,11 output


$ python postproc.py output/hanging.vtk --wireframe -b -d'u,plot_warp_scalar'

Bi-quadratic approximation, 2D, storing results in β€˜output’ directory:

$ python examples/diffusion/laplace_refine_interactive.py --order=2 output

# View solution with higher order DOFs removed.


$ python postproc.py output/hanging.vtk --wireframe -b -d'u,plot_warp_scalar'

# View full solution on a mesh adapted for visualization.


$ python postproc.py output/hanging_u.vtk --wireframe -b -d'u,plot_warp_scalar'

176 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

#!/usr/bin/env python
r"""
Example of solving Laplace's equation on a block domain refined with level 1
hanging nodes.

The domain is progressively refined towards the edge/face of the block, where
Dirichlet boundary conditions are prescribed by an oscillating function.

Find :math:`u` such that:

.. math::
\int_{\Omega} \nabla v \cdot \nabla u = 0
\;, \quad \forall s \;.

Notes
-----
The implementation of the mesh refinement with level 1 hanging nodes is a
proof-of-concept code with many unresolved issues. The main problem is the fact
that a user needs to input the cells to refine at each level, while taking care
of the following constraints:

- the level 1 hanging nodes constraint: a cell that has a less-refined


neighbour cannot be refined;
- the implementation constraint: a cell with a refined neighbour cannot be
refined.

The hanging nodes are treated by a basis transformation/DOF substitution, which


has to be applied explicitly by the user:

- call ``field.substitute_dofs(subs)`` before assembling and solving;


- then call ``field.restore_dofs()`` before saving results.

Usage Examples
--------------

Default options, 2D, storing results in 'output' directory::

$ python sfepy/examples/diffusion/laplace_refine_interactive.py output


$ python postproc.py output/hanging.vtk --wireframe -b -d'u,plot_warp_scalar'

Default options, 3D, storing results in 'output' directory::

$ python sfepy/examples/diffusion/laplace_refine_interactive.py -3 output


$ python postproc.py output/hanging.vtk --wireframe -b --3d

Finer initial domain, 2D, storing results in 'output' directory::

$ python sfepy/examples/diffusion/laplace_refine_interactive.py --shape=11,11 output


$ python postproc.py output/hanging.vtk --wireframe -b -d'u,plot_warp_scalar'

(continues on next page)

1.5. Examples 177


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


Bi-quadratic approximation, 2D, storing results in 'output' directory::

$ python sfepy/examples/diffusion/laplace_refine_interactive.py --order=2 output

# View solution with higher order DOFs removed.


$ python postproc.py output/hanging.vtk --wireframe -b -d'u,plot_warp_scalar'

# View full solution on a mesh adapted for visualization.


$ python postproc.py output/hanging_u.vtk --wireframe -b -d'u,plot_warp_scalar'
"""
from __future__ import absolute_import
from argparse import RawDescriptionHelpFormatter, ArgumentParser

import os
import sys
sys.path.append('.')
import numpy as nm

from sfepy.base.base import output, Struct


from sfepy.base.ioutils import ensure_path
from sfepy.mesh.mesh_generators import gen_block_mesh
from sfepy.discrete import (FieldVariable, Integral, Equation, Equations,
Function, Problem)
from sfepy.discrete.fem import FEDomain, Field
from sfepy.discrete.conditions import (Conditions, EssentialBC)
import sfepy.discrete.fem.refine_hanging as rh
from sfepy.solvers.ls import ScipyDirect
from sfepy.solvers.nls import Newton
from sfepy.terms import Term

def refine_towards_facet(domain0, grading, axis):


subs = None
domain = domain0
for level, coor in enumerate(grading):
refine = nm.zeros(domain.mesh.n_el, dtype=nm.uint8)

region = domain.create_region('aux',
'vertices in (%s %.10f )' % (axis, coor),
add_to_regions=False)
refine[region.cells] = 1

domain, subs = rh.refine(domain, refine, subs=subs)

return domain, subs

helps = {
'output_dir' :
'output directory',
'dims' :
'dimensions of the block [default: %(default)s]',
'shape' :
'shape (counts of nodes in x, y[, z]) of the block [default: %(default)s]',
(continues on next page)

178 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'centre' :
'centre of the block [default: %(default)s]',
'3d' :
'generate a 3D block',
'order' :
'field approximation order',
}

def main():
parser = ArgumentParser(description=__doc__.rstrip(),
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('output_dir', help=helps['output_dir'])
parser.add_argument('--dims', metavar='dims',
action='store', dest='dims',
default='1.0,1.0,1.0', help=helps['dims'])
parser.add_argument('--shape', metavar='shape',
action='store', dest='shape',
default='7,7,7', help=helps['shape'])
parser.add_argument('--centre', metavar='centre',
action='store', dest='centre',
default='0.0,0.0,0.0', help=helps['centre'])
parser.add_argument('-3', '--3d',
action='store_true', dest='is_3d',
default=False, help=helps['3d'])
parser.add_argument('--order', metavar='int', type=int,
action='store', dest='order',
default=1, help=helps['order'])
options = parser.parse_args()

dim = 3 if options.is_3d else 2


dims = nm.array(eval(options.dims), dtype=nm.float64)[:dim]
shape = nm.array(eval(options.shape), dtype=nm.int32)[:dim]
centre = nm.array(eval(options.centre), dtype=nm.float64)[:dim]

output('dimensions:', dims)
output('shape: ', shape)
output('centre: ', centre)

mesh0 = gen_block_mesh(dims, shape, centre, name='block-fem',


verbose=True)
domain0 = FEDomain('d', mesh0)

bbox = domain0.get_mesh_bounding_box()
min_x, max_x = bbox[:, 0]
eps = 1e-8 * (max_x - min_x)

cnt = (shape[0] - 1) // 2
g0 = 0.5 * dims[0]
grading = nm.array([g0 / 2**ii for ii in range(cnt)]) + eps + centre[0] - g0

domain, subs = refine_towards_facet(domain0, grading, 'x <')

(continues on next page)

1.5. Examples 179


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


omega = domain.create_region('Omega', 'all')

gamma1 = domain.create_region('Gamma1',
'vertices in (x < %.10f )' % (min_x + eps),
'facet')
gamma2 = domain.create_region('Gamma2',
'vertices in (x > %.10f )' % (max_x - eps),
'facet')

field = Field.from_args('fu', nm.float64, 1, omega,


approx_order=options.order)

if subs is not None:


field.substitute_dofs(subs)

u = FieldVariable('u', 'unknown', field)


v = FieldVariable('v', 'test', field, primary_var_name='u')

integral = Integral('i', order=2*options.order)

t1 = Term.new('dw_laplace(v, u)',
integral, omega, v=v, u=u)
eq = Equation('eq', t1)
eqs = Equations([eq])

def u_fun(ts, coors, bc=None, problem=None):


"""
Define a displacement depending on the y coordinate.
"""
if coors.shape[1] == 2:
min_y, max_y = bbox[:, 1]
y = (coors[:, 1] - min_y) / (max_y - min_y)

val = (max_y - min_y) * nm.cos(3 * nm.pi * y)

else:
min_y, max_y = bbox[:, 1]
min_z, max_z = bbox[:, 2]
y = (coors[:, 1] - min_y) / (max_y - min_y)
z = (coors[:, 2] - min_z) / (max_z - min_z)

val = ((max_y - min_y) * (max_z - min_z)


* nm.cos(3 * nm.pi * y) * (1.0 + 3.0 * (z - 0.5)**2))

return val

bc_fun = Function('u_fun', u_fun)


fix1 = EssentialBC('shift_u', gamma1, {'u.0' : bc_fun})
fix2 = EssentialBC('fix2', gamma2, {'u.all' : 0.0})

ls = ScipyDirect({})

(continues on next page)

180 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


nls = Newton({}, lin_solver=ls)

pb = Problem('heat', equations=eqs)

pb.set_bcs(ebcs=Conditions([fix1, fix2]))

pb.set_solver(nls)

state = pb.solve(save_results=False)

if subs is not None:


field.restore_dofs()

filename = os.path.join(options.output_dir, 'hanging.vtk')


ensure_path(filename)

pb.save_state(filename, state)
if options.order > 1:
pb.save_state(filename, state, linearization=Struct(kind='adaptive',
min_level=0,
max_level=8,
eps=1e-3))

if __name__ == '__main__':
main()

diffusion/laplace_shifted_periodic.py

Description
Laplace equation with shifted periodic BCs.
Display using:

./postproc.py laplace_shifted_periodic.vtk --wireframe -b -d'u,plot_warp_scalar,rel_


Λ“β†’scaling=1'

or use the –show option.


source code

#!/usr/bin/env python
"""
Laplace equation with shifted periodic BCs.

Display using::

./postproc.py laplace_shifted_periodic.vtk --wireframe -b -d'u,plot_warp_scalar,rel_


Λ“β†’scaling=1'

or use the --show option.


"""
(continues on next page)

1.5. Examples 181


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


from __future__ import absolute_import
import sys
sys.path.append('.')
from argparse import ArgumentParser, RawDescriptionHelpFormatter
import numpy as nm

from sfepy.base.base import output


from sfepy.discrete import (FieldVariable, Integral, Equation, Equations,
Function, Problem)
from sfepy.discrete.fem import FEDomain, Field
from sfepy.terms import Term
from sfepy.discrete.conditions import (Conditions, EssentialBC,
LinearCombinationBC)
from sfepy.solvers.ls import ScipyDirect
from sfepy.solvers.nls import Newton
from sfepy.mesh.mesh_generators import gen_block_mesh
import sfepy.discrete.fem.periodic as per

def run(domain, order, output_dir=''):


omega = domain.create_region('Omega', 'all')
bbox = domain.get_mesh_bounding_box()
min_x, max_x = bbox[:, 0]
min_y, max_y = bbox[:, 1]
eps = 1e-8 * (max_x - min_x)
gamma1 = domain.create_region('Gamma1',
'vertices in (x < %.10f )' % (min_x + eps),
'facet')
gamma2 = domain.create_region('Gamma2',
'vertices in (x > %.10f )' % (max_x - eps),
'facet')
gamma3 = domain.create_region('Gamma3',
'vertices in y < %.10f ' % (min_y + eps),
'facet')
gamma4 = domain.create_region('Gamma4',
'vertices in y > %.10f ' % (max_y - eps),
'facet')

field = Field.from_args('fu', nm.float64, 1, omega, approx_order=order)

u = FieldVariable('u', 'unknown', field)


v = FieldVariable('v', 'test', field, primary_var_name='u')

integral = Integral('i', order=2*order)

t1 = Term.new('dw_laplace(v, u)',
integral, omega, v=v, u=u)
eq = Equation('eq', t1)
eqs = Equations([eq])

fix1 = EssentialBC('fix1', gamma1, {'u.0' : 0.4})


fix2 = EssentialBC('fix2', gamma2, {'u.0' : 0.0})

(continues on next page)

182 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


def get_shift(ts, coors, region):
return nm.ones_like(coors[:, 0])

dof_map_fun = Function('dof_map_fun', per.match_x_line)


shift_fun = Function('shift_fun', get_shift)

sper = LinearCombinationBC('sper', [gamma3, gamma4], {'u.0' : 'u.0'},


dof_map_fun, 'shifted_periodic',
arguments=(shift_fun,))

ls = ScipyDirect({})
nls = Newton({}, lin_solver=ls)

pb = Problem('laplace', equations=eqs)
pb.set_output_dir(output_dir)

pb.set_bcs(ebcs=Conditions([fix1, fix2]), lcbcs=Conditions([sper]))

pb.set_solver(nls)

state = pb.solve()

return pb, state

helps = {
'dims' :
'dimensions of the block [default: %(default)s]',
'centre' :
'centre of the block [default: %(default)s]',
'shape' :
'numbers of vertices along each axis [default: %(default)s]',
'show' : 'show the results figure',
}

def main():
parser = ArgumentParser(description=__doc__,
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('--version', action='version', version='%(prog)s')
parser.add_argument('-d', '--dims', metavar='dims',
action='store', dest='dims',
default='[1.0, 1.0]', help=helps['dims'])
parser.add_argument('-c', '--centre', metavar='centre',
action='store', dest='centre',
default='[0.0, 0.0]', help=helps['centre'])
parser.add_argument('-s', '--shape', metavar='shape',
action='store', dest='shape',
default='[11, 11]', help=helps['shape'])
parser.add_argument('--show',
action="store_true", dest='show',
default=False, help=helps['show'])
options = parser.parse_args()

(continues on next page)

1.5. Examples 183


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


dims = nm.array(eval(options.dims), dtype=nm.float64)
centre = nm.array(eval(options.centre), dtype=nm.float64)
shape = nm.array(eval(options.shape), dtype=nm.int32)

output('dimensions:', dims)
output('centre: ', centre)
output('shape: ', shape)

mesh = gen_block_mesh(dims, shape, centre, name='block-fem')


fe_domain = FEDomain('domain', mesh)

pb, state = run(fe_domain, 1)


pb.save_state('laplace_shifted_periodic.vtk', state)

if options.show:
from sfepy.postprocess.viewer import Viewer
from sfepy.postprocess.domain_specific import DomainSpecificPlot

view = Viewer('laplace_shifted_periodic.vtk')
view(rel_scaling=1,
domain_specific={'u' : DomainSpecificPlot('plot_warp_scalar',
['rel_scaling=1'])},
is_scalar_bar=True, is_wireframe=True,
opacity=0.3)

if __name__ == '__main__':
main()

diffusion/laplace_time_ebcs.py

Description
Example explaining how to change Dirichlet boundary conditions depending on time. It is shown on the stationary
Laplace equation for temperature, so there is no dynamics, only the conditions change with time.
Five time steps are solved on a cube domain, with the temperature fixed to zero on the bottom face, and set to other
values on the left, right and top faces in different time steps.
Find 𝑑 such that:
∫︁
π‘βˆ‡π‘  Β· βˆ‡π‘‘ = 0 , βˆ€π‘  .
Ξ©

184 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Example explaining how to change Dirichlet boundary conditions depending
on time. It is shown on the stationary Laplace equation for temperature,
so there is no dynamics, only the conditions change with time.

Five time steps are solved on a cube domain, with the temperature fixed
to zero on the bottom face, and set to other values on the left, right
and top faces in different time steps.

Find :math:`t` such that:

.. math::
\int_{\Omega} c \nabla s \cdot \nabla t
= 0
\;, \quad \forall s \;.
"""
from __future__ import absolute_import
from sfepy import data_dir

filename_mesh = data_dir + '/meshes/3d/cube_medium_tetra.mesh'

(continues on next page)

1.5. Examples 185


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


options = {
'nls' : 'newton',
'ls' : 'ls',
'ts' : 'ts',

'active_only' : False,
}

regions = {
'Omega' : 'all',
'Left' : ('vertices in (x < -0.499)', 'facet'),
'Right' : ('vertices in (x > 0.499)', 'facet'),
'Bottom' : ('vertices in (z < -0.499)', 'facet'),
'Top' : ('vertices in (z > 0.499)', 'facet'),
}

materials = {
'one' : ({'val' : 1.0},),
}

fields = {
'temperature' : ('real', 1, 'Omega', 1),
}

variables = {
't' : ('unknown field', 'temperature', 0),
's' : ('test field', 'temperature', 't'),
}

ebcs = {
'fixed' : ('Bottom', {'t.all' : 0}),
't_t02' : ('Left', [(-0.5, 0.5), (2.5, 3.5)], {'t.all' : 1.0}),
't_t1' : ('Right', [(0.5, 1.5)], {'t.all' : 2.0}),
't_t4' : ('Top', 'is_ebc', {'t.all' : 3.0}),
}

def is_ebc(ts):
if ts.step in (2, 4):
return True

else:
return False

functions = {
'is_ebc' : (is_ebc,),
}

equations = {
'eq' : """dw_laplace.2.Omega( one.val, s, t ) = 0""",
}

solvers = {
(continues on next page)

186 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
}),
'ts' : ('ts.simple', {
't0' : 0.0,
't1' : 4.0,
'dt' : None,
'n_step' : 5, # has precedence over dt!

'quasistatic' : True,
'verbose' : 1,
}),
}

diffusion/poisson.py

Description
Laplace equation using the long syntax of keywords.
See the tutorial section Example Problem Description File for a detailed explanation. See diffu-
sion/poisson_short_syntax.py for the short syntax version.
Find 𝑑 such that:
∫︁
π‘βˆ‡π‘  Β· βˆ‡π‘‘ = 0 , βˆ€π‘  .
Ξ©

1.5. Examples 187


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Laplace equation using the long syntax of keywords.

See the tutorial section :ref:`poisson-example-tutorial` for a detailed


explanation. See :ref:`diffusion-poisson_short_syntax` for the short syntax
version.

Find :math:`t` such that:

.. math::
\int_{\Omega} c \nabla s \cdot \nabla t
= 0
\;, \quad \forall s \;.
"""
from __future__ import absolute_import
from sfepy import data_dir

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

material_2 = {
'name' : 'coef',
(continues on next page)

188 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'values' : {'val' : 1.0},
}

region_1000 = {
'name' : 'Omega',
'select' : 'cells of group 6',
}

region_03 = {
'name' : 'Gamma_Left',
'select' : 'vertices in (x < 0.00001)',
'kind' : 'facet',
}

region_4 = {
'name' : 'Gamma_Right',
'select' : 'vertices in (x > 0.099999)',
'kind' : 'facet',
}

field_1 = {
'name' : 'temperature',
'dtype' : 'real',
'shape' : (1,),
'region' : 'Omega',
'approx_order' : 1,
}

variable_1 = {
'name' : 't',
'kind' : 'unknown field',
'field' : 'temperature',
'order' : 0, # order in the global vector of unknowns
}

variable_2 = {
'name' : 's',
'kind' : 'test field',
'field' : 'temperature',
'dual' : 't',
}

ebc_1 = {
'name' : 't1',
'region' : 'Gamma_Left',
'dofs' : {'t.0' : 2.0},
}

ebc_2 = {
'name' : 't2',
'region' : 'Gamma_Right',
'dofs' : {'t.0' : -2.0},
(continues on next page)

1.5. Examples 189


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


}

integral_1 = {
'name' : 'i',
'order' : 2,
}

equations = {
'Temperature' : """dw_laplace.i.Omega( coef.val, s, t ) = 0"""
}

solver_0 = {
'name' : 'ls',
'kind' : 'ls.scipy_direct',
'method' : 'auto',
}

solver_1 = {
'name' : 'newton',
'kind' : 'nls.newton',

'i_max' : 1,
'eps_a' : 1e-10,
'eps_r' : 1.0,
'macheps' : 1e-16,
'lin_red' : 1e-2, # Linear system error < (eps_a * lin_red).
'ls_red' : 0.1,
'ls_red_warp' : 0.001,
'ls_on' : 1.1,
'ls_min' : 1e-5,
'check' : 0,
'delta' : 1e-6,
}

options = {
'nls' : 'newton',
'ls' : 'ls',
}

diffusion/poisson_field_dependent_material.py

Description
Laplace equation with a field-dependent material parameter.
Find 𝑇 (𝑑) for 𝑑 ∈ [0, 𝑑final ] such that:
∫︁
𝑐(𝑇 )βˆ‡π‘  Β· βˆ‡π‘‡ = 0 , βˆ€π‘  .
Ξ©

where 𝑐(𝑇 ) is the 𝑇 dependent diffusion coefficient. Each iteration calculates 𝑇 and adjusts 𝑐(𝑇 ).

190 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Laplace equation with a field-dependent material parameter.

Find :math:`T(t)` for :math:`t \in [0, t_{\rm final}]` such that:

.. math::
\int_{\Omega} c(T) \nabla s \cdot \nabla T
= 0
\;, \quad \forall s \;.

where :math:`c(T)` is the :math:`T` dependent diffusion coefficient.


Each iteration calculates :math:`T` and adjusts :math:`c(T)`.
"""
from __future__ import absolute_import
from sfepy import data_dir
from sfepy.base.base import output

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

t0 = 0.0
t1 = 0.1
(continues on next page)

1.5. Examples 191


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


n_step = 11

def get_conductivity(ts, coors, problem, equations=None, mode=None, **kwargs):


"""
Calculates the conductivity as 2+10*T and returns it.
This relation results in larger T gradients where T is small.
"""
if mode == 'qp':
# T-field values in quadrature points coordinates given by integral i
# - they are the same as in `coors` argument.
T_values = problem.evaluate('ev_integrate.i.Omega(T)',
mode='qp', verbose=False)
val = 2 + 10 * (T_values + 2)

output('conductivity: min:', val.min(), 'max:', val.max())

val.shape = (val.shape[0] * val.shape[1], 1, 1)


return {'val' : val}

materials = {
'coef' : 'get_conductivity',
}

fields = {
'temperature' : ('real', 1, 'Omega', 1),
}

variables = {
'T' : ('unknown field', 'temperature', 0),
's' : ('test field', 'temperature', 'T'),
}

regions = {
'Omega' : 'all',
'Gamma_Left' : ('vertices in (x < 0.00001)', 'facet'),
'Gamma_Right' : ('vertices in (x > 0.099999)', 'facet'),
}

ebcs = {
'T1' : ('Gamma_Left', {'T.0' : 2.0}),
'T2' : ('Gamma_Right', {'T.0' : -2.0}),
}

functions = {
'get_conductivity' : (get_conductivity,),
}

ics = {
'ic' : ('Omega', {'T.0' : 0.0}),
}

integrals = {
(continues on next page)

192 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'i' : 1,
}

equations = {
'Temperature' : """dw_laplace.i.Omega( coef.val, s, T ) = 0"""
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
'eps_r' : 1.0,
}),
'ts' : ('ts.simple', {
't0' : t0,
't1' : t1,
'dt' : None,
'n_step' : n_step, # has precedence over dt!
'quasistatic' : True,
'verbose' : 1,
}),
}

options = {
'nls' : 'newton',
'ls' : 'ls',
'ts' : 'ts',
'save_times' : 'all',
}

diffusion/poisson_functions.py

Description
Poisson equation with source term.
Find 𝑒 such that:
∫︁ ∫︁ ∫︁
π‘βˆ‡π‘£ Β· βˆ‡π‘’ = βˆ’ 𝑏𝑣 = βˆ’ 𝑓 𝑣𝑝 , βˆ€π‘£ ,
Ω Ω𝐿 Ω𝐿

where 𝑏(π‘₯) = 𝑓 (π‘₯)𝑝(π‘₯), 𝑝 is a given FE field and 𝑓 is a given general function of space.
This example demonstrates use of functions for defining material parameters, regions, parameter variables or boundary
conditions. Notably, it demonstrates the following:
1. How to define a material parameter by an arbitrary function - see the function get_pars() that evaluates 𝑓 (π‘₯)
in quadrature points.
2. How to define a known function that belongs to a given FE space (field) - this function, 𝑝(π‘₯), is defined in a FE
sense by its nodal values only - see the function get_load_variable().
In order to define the load 𝑏(π‘₯) directly, the term dw_dot should be replaced by dw_integrate.

1.5. Examples 193


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Poisson equation with source term.

Find :math:`u` such that:

.. math::
\int_{\Omega} c \nabla v \cdot \nabla u
= - \int_{\Omega_L} b v = - \int_{\Omega_L} f v p
\;, \quad \forall v \;,

where :math:`b(x) = f(x) p(x)`, :math:`p` is a given FE field and :math:`f` is


a given general function of space.

This example demonstrates use of functions for defining material parameters,


regions, parameter variables or boundary conditions. Notably, it demonstrates
the following:

1. How to define a material parameter by an arbitrary function - see the


function :func:`get_pars()` that evaluates :math:`f(x)` in quadrature
points.
2. How to define a known function that belongs to a given FE space (field) -
(continues on next page)

194 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


this function, :math:`p(x)`, is defined in a FE sense by its nodal values
only - see the function :func:`get_load_variable()`.

In order to define the load :math:`b(x)` directly, the term ``dw_dot``


should be replaced by ``dw_integrate``.
"""
from __future__ import absolute_import
import numpy as nm
from sfepy import data_dir

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

options = {
'nls' : 'newton',
'ls' : 'ls',
}

materials = {
'm' : ({'c' : 1.0},),
'load' : 'get_pars',
}

regions = {
'Omega' : 'all',
'Omega_L' : 'vertices by get_middle_ball',
'Gamma_Left' : ('vertices in (x < 0.00001)', 'facet'),
'Gamma_Right' : ('vertices in (x > 0.099999)', 'facet'),
}

fields = {
'temperature' : ('real', 1, 'Omega', 1),
'velocity' : ('real', 'vector', 'Omega', 1),
}

variables = {
'u' : ('unknown field', 'temperature', 0),
'v' : ('test field', 'temperature', 'u'),
'p' : ('parameter field', 'temperature',
{'setter' : 'get_load_variable'}),
'w' : ('parameter field', 'velocity',
{'setter' : 'get_convective_velocity'}),
}

ebcs = {
'u1' : ('Gamma_Left', {'u.0' : 'get_ebc'}),
'u2' : ('Gamma_Right', {'u.0' : -2.0}),
}

integrals = {
'i' : 1,
}

(continues on next page)

1.5. Examples 195


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


equations = {
'Laplace equation' :
"""dw_laplace.i.Omega( m.c, v, u )
- dw_convect_v_grad_s.i.Omega( v, w, u )
= - dw_dot.i.Omega_L( load.f, v, p )"""
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
}),
}

def get_pars(ts, coors, mode=None, **kwargs):


"""
Evaluate the coefficient `load.f` in quadrature points `coors` using a
function of space.

For scalar parameters, the shape has to be set to `(coors.shape[0], 1, 1)`.


"""
if mode == 'qp':
x = coors[:, 0]

val = 55.0 * (x - 0.05)

val.shape = (coors.shape[0], 1, 1)
return {'f' : val}

def get_middle_ball(coors, domain=None):


"""
Get the :math:`\Omega_L` region as a function of mesh coordinates.
"""
x, y, z = coors[:, 0], coors[:, 1], coors[:, 2]

r1 = nm.sqrt((x - 0.025)**2.0 + y**2.0 + z**2)


r2 = nm.sqrt((x - 0.075)**2.0 + y**2.0 + z**2)
flag = nm.where((r1 < 2.3e-2) | (r2 < 2.3e-2))[0]

return flag

def get_load_variable(ts, coors, region=None):


"""
Define nodal values of 'p' in the nodal coordinates `coors`.
"""
y = coors[:,1]

val = 5e5 * y
return val

def get_convective_velocity(ts, coors, region=None):


(continues on next page)

196 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


"""
Define nodal values of 'w' in the nodal coordinates `coors`.
"""
val = 100.0 * nm.ones_like(coors)

return val

def get_ebc(coors, amplitude):


"""
Define the essential boundary conditions as a function of coordinates
`coors` of region nodes.
"""
z = coors[:, 2]
val = amplitude * nm.sin(z * 2.0 * nm.pi)
return val

functions = {
'get_pars' : (get_pars,),
'get_load_variable' : (get_load_variable,),
'get_convective_velocity' : (get_convective_velocity,),
'get_middle_ball' : (get_middle_ball,),
'get_ebc' : (lambda ts, coor, bc, problem, **kwargs: get_ebc(coor, 5.0),),
}

diffusion/poisson_iga.py

Description
Poisson equation solved in a single patch NURBS domain using the isogeometric analysis (IGA) approach.
Find 𝑑 such that:
∫︁ ∫︁
π‘βˆ‡π‘  Β· βˆ‡π‘‘ = 𝑓𝑠 , βˆ€π‘  .
Ξ© Ξ©0

Try setting the Dirichlet boundary condition (ebcs) on various sides of the domain ('Gamma1', . . . , 'Gamma4').
View the results using:

$ ./postproc.py patch2d.vtk --wireframe -b


$ ./postproc.py patch2d.vtk --wireframe -b -d't,plot_warp_scalar,rel_scaling=1'

1.5. Examples 197


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Poisson equation solved in a single patch NURBS domain using the isogeometric
analysis (IGA) approach.

Find :math:`t` such that:

.. math::
\int_{\Omega} c \nabla s \cdot \nabla t
= \int_{\Omega_0} f s
\;, \quad \forall s \;.

Try setting the Dirichlet boundary condition (ebcs) on various sides of the
domain (``'Gamma1'``, ..., ``'Gamma4'``).

View the results using::

$ ./postproc.py patch2d.vtk --wireframe -b


$ ./postproc.py patch2d.vtk --wireframe -b -d't,plot_warp_scalar,rel_scaling=1'
"""
from __future__ import absolute_import
from sfepy import data_dir
(continues on next page)

198 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

filename_domain = data_dir + '/meshes/iga/patch2d.iga'

materials = {
'm' : ({'c' : 1.0, 'f' : -10.0},),
}

regions = {
'Omega' : 'all',
'Omega_0' : 'vertices in (x > 1.5)',
'Gamma1' : ('vertices of set xi00', 'facet'),
'Gamma2' : ('vertices of set xi01', 'facet'),
'Gamma3' : ('vertices of set xi10', 'facet'),
'Gamma4' : ('vertices of set xi11', 'facet'),
}

fields = {
'temperature' : ('real', 1, 'Omega', None, 'H1', 'iga'),
}

variables = {
't' : ('unknown field', 'temperature', 0),
's' : ('test field', 'temperature', 't'),
}

ebcs = {
't1' : ('Gamma3', {'t.0' : 2.0}),
't2' : ('Gamma4', {'t.0' : -2.0}),
}

integrals = {
'i' : 3,
}

equations = {
'Temperature' : """dw_laplace.i.Omega(m.c, s, t)
= dw_volume_lvf.i.Omega_0(m.f, s)"""
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
}),
}

options = {
'nls' : 'newton',
'ls' : 'ls',
}

1.5. Examples 199


SfePy Documentation, Release version: 2022.1+git.f9873484

diffusion/poisson_neumann.py

Description
The Poisson equation with Neumann boundary conditions on a part of the boundary.
Find 𝑇 such that:
∫︁ ∫︁
𝐾𝑖𝑗 βˆ‡π‘– π‘ βˆ‡π‘— 𝑝𝑇 = 𝑠𝑔 , βˆ€π‘  ,
Ξ© Γ𝑁

where 𝑔 is the given flux, 𝑔 = 𝑛 Β· 𝐾𝑖𝑗 βˆ‡π‘— 𝑇¯, and 𝐾𝑖𝑗 = 𝑐𝛿𝑖𝑗 (an isotropic medium). See the tutorial section Strong form
of Poisson’s equation and its integration for a detailed explanation.
The diffusion velocity and fluxes through various parts of the boundary are computed in the post_process() function.
On β€˜Gamma_N’ (the Neumann condition boundary part), the flux/length should correspond to the given value 𝑔 = βˆ’50,
while on β€˜Gamma_N0’ the flux should be zero. Use the β€˜refinement_level’ option (see the usage examples below) to
check the convergence of the numerical solution to those values. The total flux and the flux through β€˜Gamma_D’ (the
Dirichlet condition boundary part) are shown as well.

Usage Examples

Run with the default settings (no refinement):

python simple.py examples/diffusion/poisson_neumann.py

Refine the mesh twice:

python simple.py examples/diffusion/poisson_neumann.py -O "'refinement_level' : 2"

200 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
The Poisson equation with Neumann boundary conditions on a part of the
boundary.

Find :math:`T` such that:

.. math::
\int_{\Omega} K_{ij} \nabla_i s \nabla_j p T
= \int_{\Gamma_N} s g
\;, \quad \forall s \;,

where :math:`g` is the given flux, :math:`g = \ul{n} \cdot K_{ij} \nabla_j
\bar{T}`, and :math:`K_{ij} = c \delta_{ij}` (an isotropic medium). See the
tutorial section :ref:`poisson-weak-form-tutorial` for a detailed explanation.

The diffusion velocity and fluxes through various parts of the boundary are
computed in the :func:`post_process()` function. On 'Gamma_N' (the Neumann
condition boundary part), the flux/length should correspond to the given value
:math:`g = -50`, while on 'Gamma_N0' the flux should be zero. Use the
'refinement_level' option (see the usage examples below) to check the
convergence of the numerical solution to those values. The total flux and the
(continues on next page)

1.5. Examples 201


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


flux through 'Gamma_D' (the Dirichlet condition boundary part) are shown as
well.

Usage Examples
--------------

Run with the default settings (no refinement)::

python simple.py sfepy/examples/diffusion/poisson_neumann.py

Refine the mesh twice::

python simple.py sfepy/examples/diffusion/poisson_neumann.py -O "'refinement_level' : 2"


"""
from __future__ import absolute_import
import numpy as nm

from sfepy.base.base import output, Struct


from sfepy import data_dir

def post_process(out, pb, state, extend=False):


"""
Calculate :math:`\nabla t` and compute boundary fluxes.
"""
dv = pb.evaluate('ev_diffusion_velocity.i.Omega(m.K, t)', mode='el_avg',
verbose=False)
out['dv'] = Struct(name='output_data', mode='cell',
data=dv, dofs=None)

totals = nm.zeros(3)
for gamma in ['Gamma_N', 'Gamma_N0', 'Gamma_D']:

flux = pb.evaluate('ev_surface_flux.i.%s(m.K, t)' % gamma,


verbose=False)
area = pb.evaluate('ev_volume.i.%s(t)' % gamma, verbose=False)

flux_data = (gamma, flux, area, flux / area)


totals += flux_data[1:]

output('%8s flux: % 8.3f length: % 8.3f flux/length: % 8.3f '


% flux_data)

totals[2] = totals[0] / totals[1]


output(' total flux: % 8.3f length: % 8.3f flux/length: % 8.3f '
% tuple(totals))

return out

filename_mesh = data_dir + '/meshes/2d/cross-51-0.34.mesh'

materials = {
'flux' : ({'val' : -50.0},),
(continues on next page)

202 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'm' : ({'K' : 2.7 * nm.eye(2)},),
}

regions = {
'Omega' : 'all',
'Gamma_D' : ('vertices in (x < -0.4999)', 'facet'),
'Gamma_N0' : ('vertices in (y > 0.4999)', 'facet'),
'Gamma_N' : ('vertices of surface -s (r.Gamma_D +v r.Gamma_N0)',
'facet'),
}

fields = {
'temperature' : ('real', 1, 'Omega', 1),
}

variables = {
't' : ('unknown field', 'temperature', 0),
's' : ('test field', 'temperature', 't'),
}

ebcs = {
't1' : ('Gamma_D', {'t.0' : 5.3}),
}

integrals = {
'i' : 2
}

equations = {
'Temperature' : """
dw_diffusion.i.Omega(m.K, s, t)
= dw_integrate.i.Gamma_N(flux.val, s)
"""
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
}),
}

options = {
'nls' : 'newton',
'ls' : 'ls',

'refinement_level' : 0,
'post_process_hook' : 'post_process',
}

1.5. Examples 203


SfePy Documentation, Release version: 2022.1+git.f9873484

diffusion/poisson_parallel_interactive.py

Description
Parallel assembling and solving of a Poisson’s equation, using commands for interactive use.
Find 𝑒 such that:
∫︁ ∫︁
βˆ‡π‘£ Β· βˆ‡π‘’ = 𝑣𝑓 , βˆ€π‘  .
Ξ© Ξ©

Important Notes

β€’ This example requires petsc4py, mpi4py and (optionally) pymetis with their dependencies installed!
β€’ This example generates a number of files - do not use an existing non-empty directory for the output_dir
argument.
β€’ Use the --clear option with care!

Notes

β€’ Each task is responsible for a subdomain consisting of a set of cells (a cell region).
β€’ Each subdomain owns PETSc DOFs within a consecutive range.
β€’ When both global and task-local variables exist, the task-local variables have _i suffix.
β€’ This example does not use a nonlinear solver.
β€’ This example can serve as a template for solving a linear single-field scalar problem - just replace the equations
in create_local_problem().
β€’ The command line options are saved into <output_dir>/options.txt file.

Usage Examples

See all options:

$ python examples/diffusion/poisson_parallel_interactive.py -h

See PETSc options:

$ python examples/diffusion/poisson_parallel_interactive.py -help

Single process run useful for debugging with debug():

$ python examples/diffusion/poisson_parallel_interactive.py output-parallel

Parallel runs:

$ mpiexec -n 3 python examples/diffusion/poisson_parallel_interactive.py output-parallel␣


Λ“β†’-2 --shape=101,101

$ mpiexec -n 3 python examples/diffusion/poisson_parallel_interactive.py output-parallel␣


Λ“β†’-2 --shape=101,101 --metis

(continues on next page)

204 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

$ mpiexec -n 5 python examples/diffusion/poisson_parallel_interactive.py output-parallel␣


Λ“β†’-2 --shape=101,101 --verify --metis -ksp_monitor -ksp_converged_reason

View the results using:

$ python postproc.py output-parallel/sol.h5 --wireframe -b -d'u,plot_warp_scalar'

source code

#!/usr/bin/env python
r"""
Parallel assembling and solving of a Poisson's equation, using commands for
interactive use.

Find :math:`u` such that:

.. math::
\int_{\Omega} \nabla v \cdot \nabla u
= \int_{\Omega} v f
\;, \quad \forall s \;.

Important Notes
---------------

- This example requires petsc4py, mpi4py and (optionally) pymetis with their
dependencies installed!
- This example generates a number of files - do not use an existing non-empty
directory for the ``output_dir`` argument.
- Use the ``--clear`` option with care!

Notes
-----

- Each task is responsible for a subdomain consisting of a set of cells (a cell


region).
- Each subdomain owns PETSc DOFs within a consecutive range.
- When both global and task-local variables exist, the task-local
variables have ``_i`` suffix.
- This example does not use a nonlinear solver.
- This example can serve as a template for solving a linear single-field scalar
problem - just replace the equations in :func:`create_local_problem()`.
- The command line options are saved into <output_dir>/options.txt file.

Usage Examples
--------------

See all options::

$ python sfepy/examples/diffusion/poisson_parallel_interactive.py -h

See PETSc options::


(continues on next page)

1.5. Examples 205


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

$ python sfepy/examples/diffusion/poisson_parallel_interactive.py -help

Single process run useful for debugging with :func:`debug()


<sfepy.base.base.debug>`::

$ python sfepy/examples/diffusion/poisson_parallel_interactive.py output-parallel

Parallel runs::

$ mpiexec -n 3 python sfepy/examples/diffusion/poisson_parallel_interactive.py output-


Λ“β†’parallel -2 --shape=101,101

$ mpiexec -n 3 python sfepy/examples/diffusion/poisson_parallel_interactive.py output-


Λ“β†’parallel -2 --shape=101,101 --metis

$ mpiexec -n 5 python sfepy/examples/diffusion/poisson_parallel_interactive.py output-


Λ“β†’parallel -2 --shape=101,101 --verify --metis -ksp_monitor -ksp_converged_reason

View the results using::

$ python postproc.py output-parallel/sol.h5 --wireframe -b -d'u,plot_warp_scalar'


"""
from __future__ import absolute_import
from argparse import RawDescriptionHelpFormatter, ArgumentParser
import os
import sys
sys.path.append('.')
import csv

import numpy as nm
import matplotlib.pyplot as plt

from sfepy.base.base import output, Struct


from sfepy.base.ioutils import ensure_path, remove_files_patterns, save_options
from sfepy.base.timing import Timer
from sfepy.discrete.fem import Mesh, FEDomain, Field
from sfepy.discrete.common.region import Region
from sfepy.discrete import (FieldVariable, Material, Integral, Function,
Equation, Equations, Problem)
from sfepy.discrete.conditions import Conditions, EssentialBC
from sfepy.discrete.evaluate import apply_ebc_to_matrix
from sfepy.terms import Term
from sfepy.solvers.ls import PETScKrylovSolver

import sfepy.parallel.parallel as pl
import sfepy.parallel.plot_parallel_dofs as ppd

def create_local_problem(omega_gi, order):


"""
Local problem definition using a domain corresponding to the global region
`omega_gi`.
(continues on next page)

206 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


"""
mesh = omega_gi.domain.mesh

# All tasks have the whole mesh.


bbox = mesh.get_bounding_box()
min_x, max_x = bbox[:, 0]
eps_x = 1e-8 * (max_x - min_x)

mesh_i = Mesh.from_region(omega_gi, mesh, localize=True)


domain_i = FEDomain('domain_i', mesh_i)
omega_i = domain_i.create_region('Omega', 'all')

gamma1_i = domain_i.create_region('Gamma1',
'vertices in (x < %.10f )'
% (min_x + eps_x),
'facet', allow_empty=True)
gamma2_i = domain_i.create_region('Gamma2',
'vertices in (x > %.10f )'
% (max_x - eps_x),
'facet', allow_empty=True)

field_i = Field.from_args('fu', nm.float64, 1, omega_i,


approx_order=order)

output('number of local field DOFs:', field_i.n_nod)

u_i = FieldVariable('u_i', 'unknown', field_i)


v_i = FieldVariable('v_i', 'test', field_i, primary_var_name='u_i')

integral = Integral('i', order=2*order)

mat = Material('m', lam=10, mu=5)


t1 = Term.new('dw_laplace(m.lam, v_i, u_i)',
integral, omega_i, m=mat, v_i=v_i, u_i=u_i)

def _get_load(coors):
val = nm.ones_like(coors[:, 0])
for coor in coors.T:
val *= nm.sin(4 * nm.pi * coor)
return val

def get_load(ts, coors, mode=None, **kwargs):


if mode == 'qp':
return {'val' : _get_load(coors).reshape(coors.shape[0], 1, 1)}

load = Material('load', function=Function('get_load', get_load))

t2 = Term.new('dw_volume_lvf(load.val, v_i)',
integral, omega_i, load=load, v_i=v_i)

eq = Equation('balance', t1 - 100 * t2)


eqs = Equations([eq])
(continues on next page)

1.5. Examples 207


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

ebc1 = EssentialBC('ebc1', gamma1_i, {'u_i.all' : 0.0})


ebc2 = EssentialBC('ebc2', gamma2_i, {'u_i.all' : 0.1})

pb = Problem('problem_i', equations=eqs, active_only=False)


pb.time_update(ebcs=Conditions([ebc1, ebc2]))
pb.update_materials()

return pb

def verify_save_dof_maps(field, cell_tasks, dof_maps, id_map, options,


verbose=False):
vec = pl.verify_task_dof_maps(dof_maps, id_map, field, verbose=verbose)

order = options.order
mesh = field.domain.mesh

sfield = Field.from_args('aux', nm.float64, 'scalar', field.region,


approx_order=order)
aux = FieldVariable('aux', 'parameter', sfield,
primary_var_name='(set-to-None)')
out = aux.create_output(vec,
linearization=Struct(kind='adaptive',
min_level=order-1,
max_level=order-1,
eps=1e-8))

filename = os.path.join(options.output_dir,
'para-domains-dofs.h5')
if field.is_higher_order():
out['aux'].mesh.write(filename, out=out)

else:
mesh.write(filename, out=out)

out = Struct(name='cells', mode='cell',


data=cell_tasks[:, None, None, None])
filename = os.path.join(options.output_dir,
'para-domains-cells.h5')
mesh.write(filename, out={'cells' : out})

def solve_problem(mesh_filename, options, comm):


order = options.order

rank, size = comm.Get_rank(), comm.Get_size()

output('rank', rank, 'of', size)

stats = Struct()
timer = Timer('solve_timer')

timer.start()
(continues on next page)

208 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


mesh = Mesh.from_file(mesh_filename)
stats.t_read_mesh = timer.stop()

timer.start()
if rank == 0:
cell_tasks = pl.partition_mesh(mesh, size, use_metis=options.metis,
verbose=True)

else:
cell_tasks = None

stats.t_partition_mesh = timer.stop()

output('creating global domain and field...')


timer.start()

domain = FEDomain('domain', mesh)


omega = domain.create_region('Omega', 'all')
field = Field.from_args('fu', nm.float64, 1, omega, approx_order=order)

stats.t_create_global_fields = timer.stop()
output('...done in', timer.dt)

output('distributing field %s...' % field.name)


timer.start()

distribute = pl.distribute_fields_dofs
lfds, gfds = distribute([field], cell_tasks,
is_overlap=True,
save_inter_regions=options.save_inter_regions,
output_dir=options.output_dir,
comm=comm, verbose=True)
lfd = lfds[0]

stats.t_distribute_fields_dofs = timer.stop()
output('...done in', timer.dt)

if rank == 0:
dof_maps = gfds[0].dof_maps
id_map = gfds[0].id_map

if options.verify:
verify_save_dof_maps(field, cell_tasks,
dof_maps, id_map, options, verbose=True)

if options.plot:
ppd.plot_partitioning([None, None], field, cell_tasks, gfds[0],
options.output_dir, size)

output('creating local problem...')


timer.start()

(continues on next page)

1.5. Examples 209


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


omega_gi = Region.from_cells(lfd.cells, field.domain)
omega_gi.finalize()
omega_gi.update_shape()

pb = create_local_problem(omega_gi, order)

variables = pb.get_initial_state()
eqs = pb.equations

u_i = variables['u_i']
field_i = u_i.field

stats.t_create_local_problem = timer.stop()
output('...done in', timer.dt)

if options.plot:
ppd.plot_local_dofs([None, None], field, field_i, omega_gi,
options.output_dir, rank)

output('allocating global system...')


timer.start()

sizes, drange = pl.get_sizes(lfd.petsc_dofs_range, field.n_nod, 1)


output('sizes:', sizes)
output('drange:', drange)

pdofs = pl.get_local_ordering(field_i, lfd.petsc_dofs_conn)

output('pdofs:', pdofs)

pmtx, psol, prhs = pl.create_petsc_system(pb.mtx_a, sizes, pdofs, drange,


is_overlap=True, comm=comm,
verbose=True)

stats.t_allocate_global_system = timer.stop()
output('...done in', timer.dt)

output('evaluating local problem...')


timer.start()

variables.fill_state(0.0)
variables.apply_ebc()

rhs_i = eqs.eval_residuals(variables())
# This must be after pl.create_petsc_system() call!
mtx_i = eqs.eval_tangent_matrices(variables(), pb.mtx_a)

stats.t_evaluate_local_problem = timer.stop()
output('...done in', timer.dt)

output('assembling global system...')


timer.start()
(continues on next page)

210 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

apply_ebc_to_matrix(mtx_i, u_i.eq_map.eq_ebc)
pl.assemble_rhs_to_petsc(prhs, rhs_i, pdofs, drange, is_overlap=True,
comm=comm, verbose=True)
pl.assemble_mtx_to_petsc(pmtx, mtx_i, pdofs, drange, is_overlap=True,
comm=comm, verbose=True)

stats.t_assemble_global_system = timer.stop()
output('...done in', timer.dt)

output('creating solver...')
timer.start()

conf = Struct(method='cg', precond='gamg', sub_precond='none',


i_max=10000, eps_a=1e-50, eps_r=1e-5, eps_d=1e4, verbose=True)
status = {}
ls = PETScKrylovSolver(conf, comm=comm, mtx=pmtx, status=status)

stats.t_create_solver = timer.stop()
output('...done in', timer.dt)

output('solving...')
timer.start()

psol = ls(prhs, psol)

psol_i = pl.create_local_petsc_vector(pdofs)
gather, scatter = pl.create_gather_scatter(pdofs, psol_i, psol, comm=comm)

scatter(psol_i, psol)

sol0_i = variables() - psol_i[...]


psol_i[...] = sol0_i

gather(psol, psol_i)

stats.t_solve = timer.stop()
output('...done in', timer.dt)

output('saving solution...')
timer.start()

variables.set_state(sol0_i)
out = u_i.create_output()

filename = os.path.join(options.output_dir, 'sol_%02d.h5' % comm.rank)


pb.domain.mesh.write(filename, io='auto', out=out)

gather_to_zero = pl.create_gather_to_zero(psol)

psol_full = gather_to_zero(psol)

(continues on next page)

1.5. Examples 211


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


if comm.rank == 0:
sol = psol_full[...].copy()[id_map]

u = FieldVariable('u', 'parameter', field,


primary_var_name='(set-to-None)')

filename = os.path.join(options.output_dir, 'sol.h5')


if (order == 1) or (options.linearization == 'strip'):
out = u.create_output(sol)
mesh.write(filename, io='auto', out=out)

else:
out = u.create_output(sol, linearization=Struct(kind='adaptive',
min_level=0,
max_level=order,
eps=1e-3))

out['u'].mesh.write(filename, io='auto', out=out)

stats.t_save_solution = timer.stop()
output('...done in', timer.dt)

stats.t_total = timer.total

stats.n_dof = sizes[1]
stats.n_dof_local = sizes[0]
stats.n_cell = omega.shape.n_cell
stats.n_cell_local = omega_gi.shape.n_cell

if options.show:
plt.show()

return stats

def save_stats(filename, pars, stats, overwrite, rank, comm=None):


out = stats.to_dict()
names = sorted(out.keys())
shape_dict = {'n%d' % ii : pars.shape[ii] for ii in range(pars.dim)}
keys = ['size', 'rank', 'dim'] + list(shape_dict.keys()) + ['order'] + names

out['size'] = comm.size
out['rank'] = rank
out['dim'] = pars.dim
out.update(shape_dict)
out['order'] = pars.order

if rank == 0 and overwrite:


with open(filename, 'w') as fd:
writer = csv.DictWriter(fd, fieldnames=keys)
writer.writeheader()
writer.writerow(out)

(continues on next page)

212 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


else:
with open(filename, 'a') as fd:
writer = csv.DictWriter(fd, fieldnames=keys)
writer.writerow(out)

helps = {
'output_dir' :
'output directory',
'dims' :
'dimensions of the block [default: %(default)s]',
'shape' :
'shape (counts of nodes in x, y, z) of the block [default: %(default)s]',
'centre' :
'centre of the block [default: %(default)s]',
'2d' :
'generate a 2D rectangle, the third components of the above'
' options are ignored',
'order' :
'field approximation order',
'linearization' :
'linearization used for storing the results with approximation order > 1'
' [default: %(default)s]',
'metis' :
'use metis for domain partitioning',
'verify' :
'verify domain partitioning, save cells and DOFs of tasks'
' for visualization',
'plot' :
'make partitioning plots',
'save_inter_regions' :
'save inter-task regions for debugging partitioning problems',
'show' :
'show partitioning plots (implies --plot)',
'stats_filename' :
'name of the stats file for storing elapsed time statistics',
'new_stats' :
'create a new stats file with a header line (overwrites existing!)',
'silent' : 'do not print messages to screen',
'clear' :
'clear old solution files from output directory'
' (DANGEROUS - use with care!)',
}

def main():
parser = ArgumentParser(description=__doc__.rstrip(),
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('output_dir', help=helps['output_dir'])
parser.add_argument('--dims', metavar='dims',
action='store', dest='dims',
default='1.0,1.0,1.0', help=helps['dims'])
parser.add_argument('--shape', metavar='shape',
action='store', dest='shape',
(continues on next page)

1.5. Examples 213


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


default='11,11,11', help=helps['shape'])
parser.add_argument('--centre', metavar='centre',
action='store', dest='centre',
default='0.0,0.0,0.0', help=helps['centre'])
parser.add_argument('-2', '--2d',
action='store_true', dest='is_2d',
default=False, help=helps['2d'])
parser.add_argument('--order', metavar='int', type=int,
action='store', dest='order',
default=1, help=helps['order'])
parser.add_argument('--linearization', choices=['strip', 'adaptive'],
action='store', dest='linearization',
default='strip', help=helps['linearization'])
parser.add_argument('--metis',
action='store_true', dest='metis',
default=False, help=helps['metis'])
parser.add_argument('--verify',
action='store_true', dest='verify',
default=False, help=helps['verify'])
parser.add_argument('--plot',
action='store_true', dest='plot',
default=False, help=helps['plot'])
parser.add_argument('--show',
action='store_true', dest='show',
default=False, help=helps['show'])
parser.add_argument('--save-inter-regions',
action='store_true', dest='save_inter_regions',
default=False, help=helps['save_inter_regions'])
parser.add_argument('--stats', metavar='filename',
action='store', dest='stats_filename',
default=None, help=helps['stats_filename'])
parser.add_argument('--new-stats',
action='store_true', dest='new_stats',
default=False, help=helps['new_stats'])
parser.add_argument('--silent',
action='store_true', dest='silent',
default=False, help=helps['silent'])
parser.add_argument('--clear',
action='store_true', dest='clear',
default=False, help=helps['clear'])
options, petsc_opts = parser.parse_known_args()

if options.show:
options.plot = True

comm = pl.PETSc.COMM_WORLD

output_dir = options.output_dir

filename = os.path.join(output_dir, 'output_log_%02d.txt' % comm.rank)


if comm.rank == 0:
ensure_path(filename)
(continues on next page)

214 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


comm.barrier()

output.prefix = 'sfepy_%02d:' % comm.rank


output.set_output(filename=filename, combined=options.silent == False)

output('petsc options:', petsc_opts)

mesh_filename = os.path.join(options.output_dir, 'para.h5')

dim = 2 if options.is_2d else 3


dims = nm.array(eval(options.dims), dtype=nm.float64)[:dim]
shape = nm.array(eval(options.shape), dtype=nm.int32)[:dim]
centre = nm.array(eval(options.centre), dtype=nm.float64)[:dim]
output('dimensions:', dims)
output('shape: ', shape)
output('centre: ', centre)

if comm.rank == 0:
from sfepy.mesh.mesh_generators import gen_block_mesh

if options.clear:
remove_files_patterns(output_dir,
['*.h5', '*.mesh', '*.txt', '*.png'],
ignores=['output_log_%02d.txt' % ii
for ii in range(comm.size)],
verbose=True)

save_options(os.path.join(output_dir, 'options.txt'),
[('options', vars(options))])

mesh = gen_block_mesh(dims, shape, centre, name='block-fem',


verbose=True)
mesh.write(mesh_filename, io='auto')

comm.barrier()

output('field order:', options.order)

stats = solve_problem(mesh_filename, options, comm)


output(stats)

if options.stats_filename:
if comm.rank == 0:
ensure_path(options.stats_filename)
comm.barrier()

pars = Struct(dim=dim, shape=shape, order=options.order)


pl.call_in_rank_order(
lambda rank, comm:
save_stats(options.stats_filename, pars, stats, options.new_stats,
rank, comm),
comm
(continues on next page)

1.5. Examples 215


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


)

if __name__ == '__main__':
main()

diffusion/poisson_parametric_study.py

Description
Poisson equation.
This example demonstrates parametric study capabilities of Application classes. In particular (written in the strong
form):

π‘βˆ†π‘‘ = 𝑓 in Ω,
𝑑 = 2 on Ξ“1 , 𝑑 = βˆ’2 on Ξ“2 , 𝑓 = 1 in Ω1 , 𝑓 = 0 otherwise,

where Ω is a square domain, Ω1 ∈ Ω is a circular domain.


Now let’s see what happens if Ω1 diameter changes.
Run:

$ ./simple.py <this file>

and then look in β€˜output/r_omega1’ directory, try for example:

$ ./postproc.py output/r_omega1/circles_in_square*.vtk

Remark: this simple case could be achieved also by defining Ω1 by a time-dependent function and solve the static
problem as a time-dependent problem. However, the approach below is much more general.
Find 𝑑 such that:
∫︁
π‘βˆ‡π‘  Β· βˆ‡π‘‘ = 0 , βˆ€π‘  .
Ξ©

source code

r"""
Poisson equation.

This example demonstrates parametric study capabilities of Application


classes. In particular (written in the strong form):

.. math::
c \Delta t = f \mbox{ in } \Omega,

t = 2 \mbox{ on } \Gamma_1 \;,


t = -2 \mbox{ on } \Gamma_2 \;,
f = 1 \mbox{ in } \Omega_1 \;,
f = 0 \mbox{ otherwise,}

where :math:`\Omega` is a square domain, :math:`\Omega_1 \in \Omega` is


a circular domain.
(continues on next page)

216 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

Now let's see what happens if :math:`\Omega_1` diameter changes.

Run::

$ ./simple.py <this file>

and then look in 'output/r_omega1' directory, try for example::

$ ./postproc.py output/r_omega1/circles_in_square*.vtk

Remark: this simple case could be achieved also by defining


:math:`\Omega_1` by a time-dependent function and solve the static
problem as a time-dependent problem. However, the approach below is much
more general.

Find :math:`t` such that:

.. math::
\int_{\Omega} c \nabla s \cdot \nabla t
= 0
\;, \quad \forall s \;.
"""
from __future__ import absolute_import
import os
import numpy as nm

from sfepy import data_dir


from sfepy.base.base import output

# Mesh.
filename_mesh = data_dir + '/meshes/2d/special/circles_in_square.vtk'

# Options. The value of 'parametric_hook' is the function that does the


# parametric study.
options = {
'nls' : 'newton', # Nonlinear solver
'ls' : 'ls', # Linear solver

'parametric_hook' : 'vary_omega1_size',
'output_dir' : 'output/r_omega1',
}

# Domain and subdomains.


default_diameter = 0.25
regions = {
'Omega' : 'all',
'Gamma_1' : ('vertices in (x < -0.999)', 'facet'),
'Gamma_2' : ('vertices in (x > 0.999)', 'facet'),
'Omega_1' : 'vertices by select_circ',
}

(continues on next page)

1.5. Examples 217


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


# FE field defines the FE approximation: 2_3_P1 = 2D, P1 on triangles.
field_1 = {
'name' : 'temperature',
'dtype' : 'real',
'shape' : (1,),
'region' : 'Omega',
'approx_order' : 1,
}

# Unknown and test functions (FE sense).


variables = {
't' : ('unknown field', 'temperature', 0),
's' : ('test field', 'temperature', 't'),
}

# Dirichlet boundary conditions.


ebcs = {
't1' : ('Gamma_1', {'t.0' : 2.0}),
't2' : ('Gamma_2', {'t.0' : -2.0}),
}

# Material coefficient c and source term value f.


material_1 = {
'name' : 'coef',
'values' : {
'val' : 1.0,
}
}
material_2 = {
'name' : 'source',
'values' : {
'val' : 10.0,
}
}

# Numerical quadrature and the equation.


integral_1 = {
'name' : 'i',
'order' : 2,
}

equations = {
'Poisson' : """dw_laplace.i.Omega( coef.val, s, t )
= dw_volume_lvf.i.Omega_1( source.val, s )"""
}

# Solvers.
solver_0 = {
'name' : 'ls',
'kind' : 'ls.scipy_direct',
}

(continues on next page)

218 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


solver_1 = {
'name' : 'newton',
'kind' : 'nls.newton',

'i_max' : 1,
'eps_a' : 1e-10,
'eps_r' : 1.0,
'macheps' : 1e-16,
'lin_red' : 1e-2, # Linear system error < (eps_a * lin_red).
'ls_red' : 0.1,
'ls_red_warp' : 0.001,
'ls_on' : 1.1,
'ls_min' : 1e-5,
'check' : 0,
'delta' : 1e-6,
}

functions = {
'select_circ': (lambda coors, domain=None:
select_circ(coors[:,0], coors[:,1], 0, default_diameter),),
}

# Functions.
def select_circ( x, y, z, diameter ):
"""Select circular subdomain of a given diameter."""
r = nm.sqrt( x**2 + y**2 )

out = nm.where(r < diameter)[0]

n = out.shape[0]
if n <= 3:
raise ValueError( 'too few vertices selected! (%d)' % n )

return out

def vary_omega1_size( problem ):


"""Vary size of \Omega1. Saves also the regions into options['output_dir'].

Input:
problem: Problem instance
Return:
a generator object:
1. creates new (modified) problem
2. yields the new (modified) problem and output container
3. use the output container for some logging
4. yields None (to signal next iteration to Application)
"""
from sfepy.discrete import Problem
from sfepy.solvers.ts import get_print_info

output.prefix = 'vary_omega1_size:'

(continues on next page)

1.5. Examples 219


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


diameters = nm.linspace( 0.1, 0.6, 7 ) + 0.001
ofn_trunk, output_format = problem.ofn_trunk, problem.output_format
output_dir = problem.output_dir
join = os.path.join

conf = problem.conf
cf = conf.get_raw( 'functions' )
n_digit, aux, d_format = get_print_info( len( diameters ) + 1 )
for ii, diameter in enumerate( diameters ):
output( 'iteration %d: diameter %3.2f ' % (ii, diameter) )

cf['select_circ'] = (lambda coors, domain=None:


select_circ(coors[:,0], coors[:,1], 0, diameter),)
conf.edit('functions', cf)
problem = Problem.from_conf(conf)

problem.save_regions( join( output_dir, ('regions_' + d_format) % ii ),


['Omega_1'] )
region = problem.domain.regions['Omega_1']
if not region.has_cells():
raise ValueError('region %s has no cells!' % region.name)

ofn_trunk = ofn_trunk + '_' + (d_format % ii)


problem.setup_output(output_filename_trunk=ofn_trunk,
output_dir=output_dir,
output_format=output_format)

out = []
yield problem, out

out_problem, state = out[-1]

filename = join( output_dir,


('log_%s.txt' % d_format) % ii )
fd = open( filename, 'w' )
log_item = '$r(\Omega_1)$: %f \n' % diameter
fd.write( log_item )
fd.write( 'solution:\n' )
nm.savetxt(fd, state())
fd.close()

yield None

220 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

diffusion/poisson_periodic_boundary_condition.py

Description
Transient Laplace equation with a localized power source and periodic boundary conditions.
This example is using a mesh generated by gmsh. Both the .geo script used by gmsh to generate the file and the .mesh
file can be found in meshes.
The mesh is suitable for periodic boundary conditions. It consists of a cylinder enclosed by a box in the x and y
directions.
The cylinder will act as a power source.
The transient Laplace equation will be solved in time interval 𝑑 ∈ [0, 𝑑final ].
Find 𝑇 (𝑑) for 𝑑 ∈ [0, 𝑑final ] such that:
∫︁ ∫︁ ∫︁
πœ•π‘‡
𝑐𝑠 + 𝜎2 βˆ‡π‘  Β· βˆ‡π‘‡ = 𝑃3 𝑇 , βˆ€π‘  .
Ξ© πœ•π‘‘ Ξ© Ξ©2

source code

r"""
Transient Laplace equation with a localized power source and
periodic boundary conditions.
(continues on next page)

1.5. Examples 221


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

This example is using a mesh generated by gmsh. Both the


.geo script used by gmsh to generate the file and the .mesh
file can be found in meshes.

The mesh is suitable for periodic boundary conditions. It consists


of a cylinder enclosed by a box in the x and y directions.

The cylinder will act as a power source.

The transient Laplace equation will be solved in time interval


:math:`t \in [0, t_{\rm final}]`.

Find :math:`T(t)` for :math:`t \in [0, t_{\rm final}]` such that:

.. math::
\int_{\Omega}c s \pdiff{T}{t}
+ \int_{\Omega} \sigma_2 \nabla s \cdot \nabla T
= \int_{\Omega_2} P_3 T
\;, \quad \forall s \;.
"""

from __future__ import absolute_import


from sfepy import data_dir
import numpy as nm
import sfepy.discrete.fem.periodic as per

filename_mesh = data_dir + '/meshes/3d/cylinder_in_box.mesh'

t0 = 0.0
t1 = 1.
n_step = 11
power_per_volume =1.e2 # Heating power per volume of the cylinder
capacity_cylinder = 1. # Heat capacity of cylinder
capacity_fill = 1. # Heat capacity of filling material
conductivity_cylinder = 1. # Heat conductivity of cylinder
conductivity_fill = 1. # Heat conductivity of filling material

def cylinder_material_func(ts, coors, problem, mode=None, **kwargs):


"""
Returns the thermal conductivity, the thermal mass, and the power of the
material in the cylinder.
"""
if mode == 'qp':
shape = (coors.shape[0], 1, 1)

power = nm.empty(shape, dtype=nm.float64)


if ts.step < 5:
# The power is turned on in the first 5 steps only.
power.fill(power_per_volume)

else:
(continues on next page)

222 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


power.fill(0.0)

conductivity = nm.ones(shape) * conductivity_cylinder


capacity = nm.ones(shape) * capacity_cylinder

return {'power' : power, 'capacity' : capacity,


'conductivity' : conductivity}

materials = {
'cylinder' : 'cylinder_material_func',
'fill' : ({'capacity' : capacity_fill,
'conductivity' : conductivity_fill,},),
}

fields = {
'temperature' : ('real', 1, 'Omega', 1),
}

variables = {
'T' : ('unknown field', 'temperature', 1, 1),
's' : ('test field', 'temperature', 'T'),
}

regions = {
'Omega' : 'all',
'cylinder' : 'cells of group 444',
'fill' : 'cells of group 555',
'Gamma_Left' : ('vertices in (x < -2.4999)', 'facet'),
'y+' : ('vertices in (y >2.4999)', 'facet'),
'y-' : ('vertices in (y <-2.4999)', 'facet'),
'z+' : ('vertices in (z >0.4999)', 'facet'),
'z-' : ('vertices in (z <-0.4999)', 'facet'),
}

ebcs = {
'T1' : ('Gamma_Left', {'T.0' : 0.0}),
}

# The matching functions link the elements on each side with that on the
# opposing side.
functions = {
'cylinder_material_func' : (cylinder_material_func,),
"match_y_plane" : (per.match_y_plane,),
"match_z_plane" : (per.match_z_plane,),
}

epbcs = {
# In the y-direction
'periodic_y' : (['y+', 'y-'], {'T.0' : 'T.0'}, 'match_y_plane'),
# and in the z-direction. Due to the symmetry of the problem, this periodic
# boundary condition is actually not necessary, but we include it anyway.
'periodic_z' : (['z+', 'z-'], {'T.0' : 'T.0'}, 'match_z_plane'),
(continues on next page)

1.5. Examples 223


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


}

ics = {
'ic' : ('Omega', {'T.0' : 0.0}),
}

integrals = {
'i' : 1,
}

equations = {
'Temperature' :
"""dw_dot.i.cylinder( cylinder.capacity, s, dT/dt )
dw_dot.i.fill( fill.capacity, s, dT/dt )
dw_laplace.i.cylinder( cylinder.conductivity, s, T )
dw_laplace.i.fill( fill.conductivity, s, T )
= dw_integrate.i.cylinder( cylinder.power, s )"""
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
'eps_r' : 1.0,
}),
'ts' : ('ts.simple', {
't0' : t0,
't1' : t1,
'dt' : None,
'n_step' : n_step, # has precedence over dt!
'quasistatic' : False,
'verbose' : 1,
}),
}

options = {
'nls' : 'newton',
'ls' : 'ls',
'ts' : 'ts',
'output_dir' : 'output',
'save_times' : 'all',
'active_only' : False,
}

224 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

diffusion/poisson_short_syntax.py

Description
Laplace equation using the short syntax of keywords.
See diffusion/poisson.py for the long syntax version.
Find 𝑑 such that:
∫︁
π‘βˆ‡π‘  Β· βˆ‡π‘‘ = 0 , βˆ€π‘  .
Ξ©

source code

r"""
Laplace equation using the short syntax of keywords.

See :ref:`diffusion-poisson` for the long syntax version.

Find :math:`t` such that:

.. math::
\int_{\Omega} c \nabla s \cdot \nabla t
= 0
(continues on next page)

1.5. Examples 225


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


\;, \quad \forall s \;.
"""
from __future__ import absolute_import
from sfepy import data_dir

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

materials = {
'coef' : ({'val' : 1.0},),
}

regions = {
'Omega' : 'all', # or 'cells of group 6'
'Gamma_Left' : ('vertices in (x < 0.00001)', 'facet'),
'Gamma_Right' : ('vertices in (x > 0.099999)', 'facet'),
}

fields = {
'temperature' : ('real', 1, 'Omega', 1),
}

variables = {
't' : ('unknown field', 'temperature', 0),
's' : ('test field', 'temperature', 't'),
}

ebcs = {
't1' : ('Gamma_Left', {'t.0' : 2.0}),
't2' : ('Gamma_Right', {'t.0' : -2.0}),
}

integrals = {
'i' : 2,
}

equations = {
'Temperature' : """dw_laplace.i.Omega( coef.val, s, t ) = 0"""
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton',
{'i_max' : 1,
'eps_a' : 1e-10,
}),
}

options = {
'nls' : 'newton',
'ls' : 'ls',
}

226 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

diffusion/sinbc.py

Description
Laplace equation with Dirichlet boundary conditions given by a sine function and constants.
Find 𝑑 such that:
∫︁
π‘βˆ‡π‘  Β· βˆ‡π‘‘ = 0 , βˆ€π‘  .
Ξ©

The sfepy.discrete.fem.meshio.UserMeshIO class is used to refine the original two-element mesh before the
actual solution.
The FE polynomial basis and the approximation order can be chosen on the command-line. By default, the fifth order
Lagrange polynomial space is used, see define() arguments.
This example demonstrates how to visualize higher order approximations of the continuous solution. The adaptive
linearization is applied in order to save viewable results, see both the options keyword and the post_process()
function that computes the solution gradient. The linearization parameters can also be specified on the command line.
The Lagrange or Bernstein polynomial bases support higher order DOFs in the Dirichlet boundary conditions, unlike
the hierarchical Lobatto basis implementation, compare the results of:

python simple.py examples/diffusion/sinbc.py -d basis=lagrange


python simple.py examples/diffusion/sinbc.py -d basis=bernstein
python simple.py examples/diffusion/sinbc.py -d basis=lobatto

Use the following commands to view each of the results of the above commands (assuming default output directory
and names):

python postproc.py -b -d't,plot_warp_scalar,rel_scaling=1' 2_4_2_refined_t.vtk --


Λ“β†’wireframe

python postproc.py -b 2_4_2_refined_grad.vtk

1.5. Examples 227


SfePy Documentation, Release version: 2022.1+git.f9873484

228 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Laplace equation with Dirichlet boundary conditions given by a sine function
and constants.

Find :math:`t` such that:

.. math::
\int_{\Omega} c \nabla s \cdot \nabla t
= 0
\;, \quad \forall s \;.

The :class:`sfepy.discrete.fem.meshio.UserMeshIO` class is used to refine the


original two-element mesh before the actual solution.

The FE polynomial basis and the approximation order can be chosen on the
command-line. By default, the fifth order Lagrange polynomial space is used,
see ``define()`` arguments.

This example demonstrates how to visualize higher order approximations of the


continuous solution. The adaptive linearization is applied in order to save
viewable results, see both the options keyword and the ``post_process()``
(continues on next page)

1.5. Examples 229


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


function that computes the solution gradient. The linearization parameters can
also be specified on the command line.

The Lagrange or Bernstein polynomial bases support higher order


DOFs in the Dirichlet boundary conditions, unlike the hierarchical Lobatto
basis implementation, compare the results of::

python simple.py sfepy/examples/diffusion/sinbc.py -d basis=lagrange


python simple.py sfepy/examples/diffusion/sinbc.py -d basis=bernstein
python simple.py sfepy/examples/diffusion/sinbc.py -d basis=lobatto

Use the following commands to view each of the results of the above commands
(assuming default output directory and names)::

python postproc.py -b -d't,plot_warp_scalar,rel_scaling=1' 2_4_2_refined_t.vtk --


Λ“β†’wireframe
python postproc.py -b 2_4_2_refined_grad.vtk
"""
from __future__ import absolute_import
import numpy as nm

from sfepy import data_dir

from sfepy.base.base import output


from sfepy.discrete.fem import Mesh, FEDomain
from sfepy.discrete.fem.meshio import UserMeshIO, MeshIO
from sfepy.homogenization.utils import define_box_regions
from six.moves import range

base_mesh = data_dir + '/meshes/elements/2_4_2.mesh'

def mesh_hook(mesh, mode):


"""
Load and refine a mesh here.
"""
if mode == 'read':
mesh = Mesh.from_file(base_mesh)
domain = FEDomain(mesh.name, mesh)
for ii in range(3):
output('refine %d...' % ii)
domain = domain.refine()
output('... %d nodes %d elements'
% (domain.shape.n_nod, domain.shape.n_el))

domain.mesh.name = '2_4_2_refined'

return domain.mesh

elif mode == 'write':


pass

def post_process(out, pb, state, extend=False):


(continues on next page)

230 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


"""
Calculate gradient of the solution.
"""
from sfepy.discrete.fem.fields_base import create_expression_output

aux = create_expression_output('ev_grad.ie.Elements( t )',


'grad', 'temperature',
pb.fields, pb.get_materials(),
pb.get_variables(), functions=pb.functions,
mode='qp', verbose=False,
min_level=0, max_level=5, eps=1e-3)
out.update(aux)

return out

def define(order=5, basis='lagrange', min_level=0, max_level=5, eps=1e-3):

filename_mesh = UserMeshIO(mesh_hook)

# Get the mesh bounding box.


io = MeshIO.any_from_filename(base_mesh)
bbox, dim = io.read_bounding_box(ret_dim=True)

options = {
'nls' : 'newton',
'ls' : 'ls',
'post_process_hook' : 'post_process',
'linearization' : {
'kind' : 'adaptive',
'min_level' : min_level, # Min. refinement level applied everywhere.
'max_level' : max_level, # Max. refinement level.
'eps' : eps, # Relative error tolerance.
},
}

materials = {
'coef' : ({'val' : 1.0},),
}

regions = {
'Omega' : 'all',
}
regions.update(define_box_regions(dim, bbox[0], bbox[1], 1e-5))

fields = {
'temperature' : ('real', 1, 'Omega', order, 'H1', basis),
}

variables = {
't' : ('unknown field', 'temperature', 0),
's' : ('test field', 'temperature', 't'),
}
(continues on next page)

1.5. Examples 231


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

amplitude = 1.0
def ebc_sin(ts, coor, **kwargs):
x0 = 0.5 * (coor[:, 1].min() + coor[:, 1].max())
val = amplitude * nm.sin( (coor[:, 1] - x0) * 2. * nm.pi )
return val

ebcs = {
't1' : ('Left', {'t.0' : 'ebc_sin'}),
't2' : ('Right', {'t.0' : -0.5}),
't3' : ('Top', {'t.0' : 1.0}),
}

functions = {
'ebc_sin' : (ebc_sin,),
}

equations = {
'Temperature' : """dw_laplace.10.Omega(coef.val, s, t) = 0"""
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
}),
}

return locals()

diffusion/time_advection_diffusion.py

Description
The transient advection-diffusion equation with a given divergence-free advection velocity.
Find 𝑒 such that:
∫︁ ∫︁ ∫︁
πœ•π‘’
𝑠 + π‘ βˆ‡ Β· (𝑣𝑒) + π·βˆ‡π‘  Β· βˆ‡π‘’ = 0 , βˆ€π‘  .
Ξ© πœ•π‘‘ Ξ© Ξ©

View the results using:

python postproc.py square_tri2.*.vtk -b --wireframe

232 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
The transient advection-diffusion equation with a given divergence-free
advection velocity.

Find :math:`u` such that:

.. math::
\int_{\Omega} s \pdiff{u}{t}
+ \int_{\Omega} s \nabla \cdot \left(\ul{v} u \right)
+ \int_{\Omega} D \nabla s \cdot \nabla u
= 0
\;, \quad \forall s \;.

View the results using::

python postproc.py square_tri2.*.vtk -b --wireframe


"""
from __future__ import absolute_import
from sfepy import data_dir

filename_mesh = data_dir + '/meshes/2d/square_tri2.mesh'


(continues on next page)

1.5. Examples 233


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

regions = {
'Omega' : 'all', # or 'cells of group 6'
'Gamma_Left' : ('vertices in (x < -0.99999)', 'facet'),
'Gamma_Right' : ('vertices in (x > 0.99999)', 'facet'),
}

fields = {
'concentration' : ('real', 1, 'Omega', 1),
}

variables = {
'u' : ('unknown field', 'concentration', 0, 1),
's' : ('test field', 'concentration', 'u'),
}

ebcs = {
'u1' : ('Gamma_Left', {'u.0' : 2.0}),
'u2' : ('Gamma_Right', {'u.0' : 0.0}),
}

# Units: D: 0.0001 m^2 / day, v: [0.1, 0] m / day -> time in days.


materials = {
'm' : ({'D' : 0.0001, 'v' : [[0.1], [0.0]]},),
}

integrals = {
'i' : 2,
}

equations = {
'advection-diffusion' :
"""
dw_dot.i.Omega(s, du/dt)
+ dw_advect_div_free.i.Omega(m.v, s, u)
+ dw_laplace.i.Omega(m.D, s, u)
= 0
"""
}

solvers = {
'ts' : ('ts.simple', {
't0' : 0.0,
't1' : 10.0,
'dt' : None,
'n_step' : 11, # Has precedence over dt.
'verbose' : 1,
}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-10,
}),
(continues on next page)

234 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'ls' : ('ls.scipy_direct', {}),
}

options = {
'ts' : 'ts',
'nls' : 'newton',
'ls' : 'ls',
'save_times' : 'all',
}

diffusion/time_poisson.py

Description
Transient Laplace equation with non-constant initial conditions given by a function.
Find 𝑇 (𝑑) for 𝑑 ∈ [0, 𝑑final ] such that:
∫︁ ∫︁
πœ•π‘‡
𝑠 + π‘βˆ‡π‘  Β· βˆ‡π‘‡ = 0 , βˆ€π‘  .
Ξ© πœ•π‘‘ Ξ©

source code

1.5. Examples 235


SfePy Documentation, Release version: 2022.1+git.f9873484

r"""
Transient Laplace equation with non-constant initial conditions given by a
function.

Find :math:`T(t)` for :math:`t \in [0, t_{\rm final}]` such that:

.. math::
\int_{\Omega} s \pdiff{T}{t}
+ \int_{\Omega} c \nabla s \cdot \nabla T
= 0
\;, \quad \forall s \;.
"""
from __future__ import absolute_import
from sfepy import data_dir

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

t0 = 0.0
t1 = 0.1
n_step = 11

material_2 = {
'name' : 'coef',
'values' : {'val' : 0.01},
'kind' : 'stationary', # 'stationary' or 'time-dependent'
}

field_1 = {
'name' : 'temperature',
'dtype' : 'real',
'shape' : (1,),
'region' : 'Omega',
'approx_order' : 1,
}

variable_1 = {
'name' : 'T',
'kind' : 'unknown field',
'field' : 'temperature',
'order' : 0,
'history' : 1,
}
variable_2 = {
'name' : 's',
'kind' : 'test field',
'field' : 'temperature',
'dual' : 'T',
}

regions = {
'Omega' : 'all',
'Gamma_Left' : ('vertices in (x < 0.00001)', 'facet'),
'Gamma_Right' : ('vertices in (x > 0.099999)', 'facet'),
(continues on next page)

236 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


}

ebcs = {
'T1': ('Gamma_Left', {'T.0' : 2.0}),
'T2': ('Gamma_Right', {'T.0' : -2.0}),
}

def get_ic(coor, ic):


"""Non-constant initial condition."""
import numpy as nm
# Normalize x coordinate.
mi, ma = coor[:,0].min(), coor[:,0].max()
nx = (coor[:,0] - mi) / (ma - mi)
return nm.where( (nx > 0.25) & (nx < 0.75 ), 8.0 * (nx - 0.5), 0.0 )

functions = {
'get_ic' : (get_ic,),
}

ics = {
'ic' : ('Omega', {'T.0' : 'get_ic'}),
}

integral_1 = {
'name' : 'i',
'order' : 1,
}

equations = {
'Temperature' :
"""dw_dot.i.Omega( s, dT/dt )
+ dw_laplace.i.Omega( coef.val, s, T ) = 0"""
}

solver_0 = {
'name' : 'ls',
'kind' : 'ls.scipy_direct',
'use_presolve' : True,
}

solver_1 = {
'name' : 'newton',
'kind' : 'nls.newton',

'i_max' : 1,
'eps_a' : 1e-10,
'eps_r' : 1.0,
'macheps' : 1e-16,
'lin_red' : 1e-2, # Linear system error < (eps_a * lin_red).
'ls_red' : 0.1,
'ls_red_warp' : 0.001,
'ls_on' : 1.1,
(continues on next page)

1.5. Examples 237


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'ls_min' : 1e-5,
'check' : 0,
'delta' : 1e-6,
'is_linear' : True,
}

solver_2 = {
'name' : 'ts',
'kind' : 'ts.simple',

't0' : t0,
't1' : t1,
'dt' : None,
'n_step' : n_step, # has precedence over dt!
'verbose' : 1,
}

options = {
'nls' : 'newton',
'ls' : 'ls',
'ts' : 'ts',
'save_times' : 'all',
}

diffusion/time_poisson_explicit.py

Description
Transient Laplace equation.
The same example as time_poisson.py, but using the short syntax of keywords, and explicit time-stepping.
Find 𝑇 (𝑑) for 𝑑 ∈ [0, 𝑑final ] such that:
∫︁ ∫︁
πœ•π‘‡
𝑠 + π‘βˆ‡π‘  Β· βˆ‡π‘‡ = 0 , βˆ€π‘  .
Ξ© πœ•π‘‘ Ξ©

238 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

r"""
Transient Laplace equation.

The same example as time_poisson.py, but using the short syntax of keywords,
and explicit time-stepping.

Find :math:`T(t)` for :math:`t \in [0, t_{\rm final}]` such that:

.. math::
\int_{\Omega} s \pdiff{T}{t}
+ \int_{\Omega} c \nabla s \cdot \nabla T
= 0
\;, \quad \forall s \;.
"""
from __future__ import absolute_import
from sfepy import data_dir

from sfepy.examples.diffusion.time_poisson import get_ic

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

(continues on next page)

1.5. Examples 239


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


materials = {
'coef' : ({'val' : 0.01},),
}

regions = {
'Omega' : 'all',
'Gamma_Left' : ('vertices in (x < 0.00001)', 'facet'),
'Gamma_Right' : ('vertices in (x > 0.099999)', 'facet'),
}

fields = {
'temperature' : ('real', 1, 'Omega', 1),
}

variables = {
'T' : ('unknown field', 'temperature', 0, 1),
's' : ('test field', 'temperature', 'T'),
}

ebcs = {
't1' : ('Gamma_Left', {'T.0' : 2.0}),
't2' : ('Gamma_Right', {'T.0' : -2.0}),
}

ics = {
'ic' : ('Omega', {'T.0' : 'get_ic'}),
}

functions = {
'get_ic' : (get_ic,),
}

integrals = {
'i' : 1,
}

equations = {
'Temperature' :
"""dw_dot.i.Omega( s, dT/dt )
+ dw_laplace.i.Omega( coef.val, s, T[-1] ) = 0"""
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'is_linear' : True,
}),
'ts' : ('ts.simple', {
't0' : 0.0,
't1' : 0.07,
'dt' : 0.00002,
(continues on next page)

240 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'n_step' : None,
'verbose' : 1,
}),
}

options = {
'ls' : 'ls',
'ts' : 'ts',
'save_times' : 100,
'output_format' : 'h5',
}

diffusion/time_poisson_interactive.py

Description
Transient Laplace equation (heat equation) with non-constant initial conditions given by a function, using commands
for interactive use.
The script allows setting various simulation parameters, namely:
β€’ the diffusivity coefficient
β€’ the max. initial condition value
β€’ temperature field approximation order
β€’ uniform mesh refinement
The example shows also how to probe the results.
In the SfePy top-level directory the following command can be used to get usage information:

python examples/diffusion/time_poisson_interactive.py -h

source code

#!/usr/bin/env python
"""
Transient Laplace equation (heat equation) with non-constant initial conditions
given by a function, using commands for interactive use.

The script allows setting various simulation parameters, namely:

- the diffusivity coefficient


- the max. initial condition value
- temperature field approximation order
- uniform mesh refinement

The example shows also how to probe the results.

In the SfePy top-level directory the following command can be used to get usage
information::

python sfepy/examples/diffusion/time_poisson_interactive.py -h
(continues on next page)

1.5. Examples 241


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


"""
from __future__ import absolute_import
import sys
from six.moves import range
sys.path.append('.')
from argparse import ArgumentParser, RawDescriptionHelpFormatter

import numpy as nm
import matplotlib.pyplot as plt

from sfepy.base.base import assert_, output, ordered_iteritems, IndexedStruct


from sfepy.discrete import (FieldVariable, Material, Integral, Function,
Equation, Equations, Problem)
from sfepy.discrete.problem import prepare_matrix
from sfepy.discrete.fem import Mesh, FEDomain, Field
from sfepy.terms import Term
from sfepy.discrete.conditions import Conditions, EssentialBC, InitialCondition
from sfepy.solvers.ls import ScipyDirect
from sfepy.solvers.nls import Newton
from sfepy.solvers.ts_solvers import SimpleTimeSteppingSolver
from sfepy.discrete.probes import LineProbe, CircleProbe
from sfepy.discrete.projections import project_by_component

def gen_probes(problem):
"""
Define a line probe and a circle probe.
"""
# Use enough points for higher order approximations.
n_point = 1000

p0, p1 = nm.array([0.0, 0.0, 0.0]), nm.array([0.1, 0.0, 0.0])


line = LineProbe(p0, p1, n_point, share_geometry=True)
# Workaround current probe code shortcoming.
line.set_options(close_limit=0.5)

centre = 0.5 * (p0 + p1)


normal = [0.0, 1.0, 0.0]
r = 0.019
circle = CircleProbe(centre, normal, r, n_point, share_geometry=True)
circle.set_options(close_limit=0.0)

probes = [line, circle]


labels = ['%s -> %s' % (p0, p1),
'circle(%s, %s, %s' % (centre, normal, r)]

return probes, labels

def probe_results(ax_num, T, dvel, probe, label):


"""
Probe the results using the given probe and plot the probed values.
"""
results = {}
(continues on next page)

242 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

pars, vals = probe(T)


results['T'] = (pars, vals)

pars, vals = probe(dvel)


results['dvel'] = (pars, vals)

fig = plt.figure(1)

ax = plt.subplot(2, 2, 2 * ax_num + 1)
ax.cla()
pars, vals = results['T']
ax.plot(pars, vals, label=r'$T$', lw=1, ls='-', marker='+', ms=3)
dx = 0.05 * (pars[-1] - pars[0])
ax.set_xlim(pars[0] - dx, pars[-1] + dx)
ax.set_ylabel('temperature')
ax.set_xlabel('probe %s' % label, fontsize=8)
ax.legend(loc='best', fontsize=10)

ax = plt.subplot(2, 2, 2 * ax_num + 2)
ax.cla()
pars, vals = results['dvel']
for ic in range(vals.shape[1]):
ax.plot(pars, vals[:, ic], label=r'$w_{%d}$' % (ic + 1),
lw=1, ls='-', marker='+', ms=3)
dx = 0.05 * (pars[-1] - pars[0])
ax.set_xlim(pars[0] - dx, pars[-1] + dx)
ax.set_ylabel('diffusion velocity')
ax.set_xlabel('probe %s' % label, fontsize=8)
ax.legend(loc='best', fontsize=10)

return fig, results

helps = {
'diffusivity' : 'the diffusivity coefficient [default: %(default)s]',
'ic_max' : 'the max. initial condition value [default: %(default)s]',
'order' : 'temperature field approximation order [default: %(default)s]',
'refine' : 'uniform mesh refinement level [default: %(default)s]',
'probe' : 'probe the results',
'show' : 'show the probing results figure, if --probe is used',
}

def main():
from sfepy import data_dir

parser = ArgumentParser(description=__doc__,
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('--version', action='version', version='%(prog)s')
parser.add_argument('--diffusivity', metavar='float', type=float,
action='store', dest='diffusivity',
default=1e-5, help=helps['diffusivity'])
parser.add_argument('--ic-max', metavar='float', type=float,
(continues on next page)

1.5. Examples 243


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


action='store', dest='ic_max',
default=2.0, help=helps['ic_max'])
parser.add_argument('--order', metavar='int', type=int,
action='store', dest='order',
default=2, help=helps['order'])
parser.add_argument('-r', '--refine', metavar='int', type=int,
action='store', dest='refine',
default=0, help=helps['refine'])
parser.add_argument('-p', '--probe',
action="store_true", dest='probe',
default=False, help=helps['probe'])
parser.add_argument('-s', '--show',
action="store_true", dest='show',
default=False, help=helps['show'])
options = parser.parse_args()

assert_((0 < options.order),


'temperature approximation order must be at least 1!')

output('using values:')
output(' diffusivity:', options.diffusivity)
output(' max. IC value:', options.ic_max)
output('uniform mesh refinement level:', options.refine)

mesh = Mesh.from_file(data_dir + '/meshes/3d/cylinder.mesh')


domain = FEDomain('domain', mesh)

if options.refine > 0:
for ii in range(options.refine):
output('refine %d...' % ii)
domain = domain.refine()
output('... %d nodes %d elements'
% (domain.shape.n_nod, domain.shape.n_el))

omega = domain.create_region('Omega', 'all')


left = domain.create_region('Left',
'vertices in x < 0.00001', 'facet')
right = domain.create_region('Right',
'vertices in x > 0.099999', 'facet')

field = Field.from_args('fu', nm.float64, 'scalar', omega,


approx_order=options.order)

T = FieldVariable('T', 'unknown', field, history=1)


s = FieldVariable('s', 'test', field, primary_var_name='T')

m = Material('m', diffusivity=options.diffusivity * nm.eye(3))

integral = Integral('i', order=2*options.order)

t1 = Term.new('dw_diffusion(m.diffusivity, s, T)',
integral, omega, m=m, s=s, T=T)
(continues on next page)

244 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


t2 = Term.new('dw_dot(s, dT/dt)',
integral, omega, s=s, T=T)
eq = Equation('balance', t1 + t2)
eqs = Equations([eq])

# Boundary conditions.
ebc1 = EssentialBC('T1', left, {'T.0' : 2.0})
ebc2 = EssentialBC('T2', right, {'T.0' : -2.0})

# Initial conditions.
def get_ic(coors, ic):
x, y, z = coors.T
return 2 - 40.0 * x + options.ic_max * nm.sin(4 * nm.pi * x / 0.1)
ic_fun = Function('ic_fun', get_ic)
ic = InitialCondition('ic', omega, {'T.0' : ic_fun})

pb = Problem('heat', equations=eqs)
pb.set_bcs(ebcs=Conditions([ebc1, ebc2]))
pb.set_ics(Conditions([ic]))

variables = pb.get_initial_state()
init_fun, prestep_fun, _poststep_fun = pb.get_tss_functions()

ls = ScipyDirect({})
nls_status = IndexedStruct()
nls = Newton({'is_linear' : True}, lin_solver=ls, status=nls_status)
tss = SimpleTimeSteppingSolver({'t0' : 0.0, 't1' : 100.0, 'n_step' : 11},
nls=nls, context=pb, verbose=True)
pb.set_solver(tss)

if options.probe:
# Prepare probe data.
probes, labels = gen_probes(pb)

ev = pb.evaluate
order = 2 * (options.order - 1)

gfield = Field.from_args('gu', nm.float64, 'vector', omega,


approx_order=options.order - 1)
dvel = FieldVariable('dvel', 'parameter', gfield,
primary_var_name='(set-to-None)')
cfield = Field.from_args('gu', nm.float64, 'scalar', omega,
approx_order=options.order - 1)
component = FieldVariable('component', 'parameter', cfield,
primary_var_name='(set-to-None)')

nls_options = {'eps_a' : 1e-16, 'i_max' : 1}

suffix = tss.ts.suffix
def poststep_fun(ts, vec):
_poststep_fun(ts, vec)

(continues on next page)

1.5. Examples 245


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


# Probe the solution.
dvel_qp = ev('ev_diffusion_velocity.%d.Omega(m.diffusivity, T)'
% order, copy_materials=False, mode='qp')
project_by_component(dvel, dvel_qp, component, order,
nls_options=nls_options)

all_results = []
for ii, probe in enumerate(probes):
fig, results = probe_results(ii, T, dvel, probe, labels[ii])

all_results.append(results)

plt.tight_layout()
fig.savefig('time_poisson_interactive_probe_%s.png'
% (suffix % ts.step), bbox_inches='tight')

for ii, results in enumerate(all_results):


output('probe %d (%s):' % (ii, probes[ii].name))
output.level += 2
for key, res in ordered_iteritems(results):
output(key + ':')
val = res[1]
output(' min: %+.2e, mean: %+.2e, max: %+.2e'
% (val.min(), val.mean(), val.max()))
output.level -= 2

else:
poststep_fun = _poststep_fun

pb.time_update(tss.ts)
variables.apply_ebc()

# This is required if {'is_linear' : True} is passed to Newton.


mtx = prepare_matrix(pb, variables)
pb.try_presolve(mtx)

tss_status = IndexedStruct()
tss(variables.get_state(pb.active_only, force=True),
init_fun=init_fun, prestep_fun=prestep_fun, poststep_fun=poststep_fun,
status=tss_status)

output(tss_status)

if options.show:
plt.show()

if __name__ == '__main__':
main()

246 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

homogenization

homogenization/homogenization_opt.py

Description
missing description!
source code

from __future__ import absolute_import


import numpy as nm

import sfepy.discrete.fem.periodic as per


from sfepy.discrete.fem.mesh import Mesh
from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson
from sfepy.homogenization.utils import define_box_regions
import sfepy.homogenization.coefs_base as cb
from sfepy import data_dir

# material function
def get_mat(coors, mode, pb):
if mode == 'qp':
cnf = pb.conf
# get material coefficients
if hasattr(cnf, 'opt_data'):
# from optim.
E_f, nu_f, E_m, nu_m = cnf.opt_data['mat_params']
else:
# given values
E_f, nu_f, E_m, nu_m = 160.e9, 0.28, 5.e9, 0.45

nqp = coors.shape[0]
nel = pb.domain.mesh.n_el
nqpe = nqp // nel
out = nm.zeros((nqp, 6, 6), dtype=nm.float64)

# set values - matrix


D_m = stiffness_from_youngpoisson(3, E_m, nu_m)
Ym = pb.domain.regions['Ym'].get_cells()
idx0 = (nm.arange(nqpe)[:,nm.newaxis] * nm.ones((1, Ym.shape[0]),
dtype=nm.int32)).T.flatten()
idxs = (Ym[:,nm.newaxis] * nm.ones((1, nqpe),
dtype=nm.int32)).flatten() * nqpe
out[idxs + idx0,...] = D_m

# set values - fiber


D_f = stiffness_from_youngpoisson(3, E_f, nu_f)
Yf = pb.domain.regions['Yf'].get_cells()
idx0 = (nm.arange(nqpe)[:,nm.newaxis] * nm.ones((1, Yf.shape[0]),
dtype=nm.int32)).T.flatten()
idxs = (Yf[:,nm.newaxis] * nm.ones((1, nqpe),
dtype=nm.int32)).flatten() * nqpe
out[idxs + idx0,...] = D_f
(continues on next page)

1.5. Examples 247


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

return {'D': out}

def optimization_hook(pb):
cnf = pb.conf
out = []
yield pb, out

if hasattr(cnf, 'opt_data'):
# store homogenized tensor
pb.conf.opt_data['D_homog'] = out[-1].D.copy()

yield None

def define(is_opt=False):
filename_mesh = data_dir + '/meshes/3d/matrix_fiber_rand.vtk'

mesh = Mesh.from_file(filename_mesh)
bbox = mesh.get_bounding_box()

regions = {
'Y' : 'all',
'Ym' : ('cells of group 7', 'cell'),
'Yf' : ('r.Y -c r.Ym', 'cell'),
}

regions.update(define_box_regions(3, bbox[0], bbox[1]))

functions = {
'get_mat': (lambda ts, coors, mode=None, problem=None, **kwargs:
get_mat(coors, mode, problem),),
'match_x_plane' : (per.match_x_plane,),
'match_y_plane' : (per.match_y_plane,),
'match_z_plane' : (per.match_z_plane,),
}

materials = {
'mat': 'get_mat',
}

fields = {
'corrector' : ('real', 3, 'Y', 1),
}

variables = {
'u': ('unknown field', 'corrector'),
'v': ('test field', 'corrector', 'u'),
'Pi': ('parameter field', 'corrector', 'u'),
'Pi1': ('parameter field', 'corrector', '(set-to-None)'),
'Pi2': ('parameter field', 'corrector', '(set-to-None)'),
}

(continues on next page)

248 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


ebcs = {
'fixed_u' : ('Corners', {'u.all' : 0.0}),
}

epbcs = {
'periodic_x' : (['Left', 'Right'], {'u.all' : 'u.all'}, 'match_x_plane'),
'periodic_y' : (['Near', 'Far'], {'u.all' : 'u.all'}, 'match_y_plane'),
'periodic_z' : (['Top', 'Bottom'], {'u.all' : 'u.all'}, 'match_z_plane'),
}

all_periodic = ['periodic_%s' % ii for ii in ['x', 'y', 'z'][:3]]

options = {
'coefs': 'coefs',
'requirements': 'requirements',
'volume': { 'variables' : ['u'], 'expression' : 'ev_volume.5.Y( u )' },
'output_dir': 'output',
'coefs_filename': 'coefs_le',
}

equation_corrs = {
'balance_of_forces':
"""dw_lin_elastic.5.Y(mat.D, v, u)
= - dw_lin_elastic.5.Y(mat.D, v, Pi)"""
}

coefs = {
'D' : {
'requires' : ['pis', 'corrs_rs'],
'expression' : 'dw_lin_elastic.5.Y(mat.D, Pi1, Pi2 )',
'set_variables': [('Pi1', ('pis', 'corrs_rs'), 'u'),
('Pi2', ('pis', 'corrs_rs'), 'u')],
'class' : cb.CoefSymSym,
},
'vol': {
'regions': ['Ym', 'Yf'],
'expression': 'ev_volume.5.%s(u)',
'class': cb.VolumeFractions,
},
'filenames' : {},
}

requirements = {
'pis' : {
'variables' : ['u'],
'class' : cb.ShapeDimDim,
},
'corrs_rs' : {
'requires' : ['pis'],
'ebcs' : ['fixed_u'],
'epbcs' : all_periodic,
'equations' : equation_corrs,
(continues on next page)

1.5. Examples 249


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'set_variables' : [('Pi', 'pis', 'u')],
'class' : cb.CorrDimDim,
'save_name' : 'corrs_le',
},
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-4,
'problem': 'linear',
})
}

if is_opt:
options.update({
'parametric_hook': 'optimization_hook',
'float_format': '%.16e',
})

return locals()

homogenization/linear_elastic_mM.py

Description
missing description!

250 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

from __future__ import absolute_import


import os
from sfepy import data_dir, base_dir
from sfepy.base.base import nm
from sfepy.homogenization.micmac import get_homog_coefs_linear
from sfepy.homogenization.recovery import save_recovery_region,\
recover_micro_hook

def post_process(out, pb, state, extend=False):


from sfepy.base.base import Struct

if isinstance(state, dict):
pass
else:
stress = pb.evaluate('ev_cauchy_stress.i.Omega(solid.D, u)',
mode='el_avg')
strain = pb.evaluate('ev_cauchy_strain.i.Omega(u)',
mode='el_avg')
out['cauchy_strain'] = Struct(name='output_data',
mode='cell', data=strain,
dofs=None)
(continues on next page)

1.5. Examples 251


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


out['cauchy_stress'] = Struct(name='output_data',
mode='cell', data=stress,
dofs=None)

if pb.conf.options.get('recover_micro', False):
rname = pb.conf.options.recovery_region
region = pb.domain.regions[rname]

filename = os.path.join(os.path.dirname(pb.get_output_name()),
'recovery_region.vtk')
save_recovery_region(pb, rname, filename=filename);

rstrain = pb.evaluate('ev_cauchy_strain.i.%s(u)' % rname,


mode='el_avg')

recover_micro_hook(pb.conf.options.micro_filename,
region, {'strain' : rstrain},
output_dir=pb.conf.options.output_dir)

return out

def get_elements(coors, domain=None):


return nm.arange(50, domain.shape.n_el, 100)

regenerate = True

def get_homog(ts, coors, mode=None,


equations=None, term=None, problem=None, **kwargs):
global regenerate

out = get_homog_coefs_linear(ts, coors, mode, regenerate=regenerate,


micro_filename=options['micro_filename'],
output_dir=problem.conf.options.output_dir)
regenerate = False

return out

functions = {
'get_elements' : (get_elements,),
'get_homog' : (get_homog,),
}

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

regions = {
'Omega' : 'all',
'Left' : ('vertices in (x < 0.001)', 'facet'),
'Right' : ('vertices in (x > 0.099)', 'facet'),
'Recovery' : 'cells by get_elements',
}

materials = {
(continues on next page)

252 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'solid' : 'get_homog',
}

fields = {
'3_displacement' : ('real', 3, 'Omega', 1),
}

integrals = {
'i' : 1,
}

variables = {
'u' : ('unknown field', '3_displacement', 0),
'v' : ('test field', '3_displacement', 'u'),
}

ebcs = {
'Fixed' : ('Left', {'u.all' : 0.0}),
'PerturbedSurface' : ('Right', {'u.0' : 0.02, 'u.1' : 0.0, 'u.2' : 0.0}),
}

equations = {
'balance_of_forces' :
"""dw_lin_elastic.i.Omega(solid.D, v, u) = 0""",
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-6,
}),
}

micro_filename = base_dir \
+ '/examples/homogenization/linear_homogenization_up.py'

options = {
'nls' : 'newton',
'ls' : 'ls',
'output_dir' : 'output',
'post_process_hook' : 'post_process',
'output_prefix' : 'macro:',
'recover_micro': True,
'recovery_region' : 'Recovery',
'micro_filename' : micro_filename,
}

1.5. Examples 253


SfePy Documentation, Release version: 2022.1+git.f9873484

homogenization/linear_elasticity_opt.py

Description
missing description!
source code

from __future__ import absolute_import


import numpy as nm

from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson


from sfepy.discrete.fem.meshio import UserMeshIO
from sfepy.mesh.mesh_generators import gen_block_mesh
from sfepy import data_dir

def mesh_hook(mesh, mode):


if mode == 'read':
mesh = gen_block_mesh([0.0098, 0.0011, 0.1], [5, 3, 17],
[0, 0, 0.05], name='specimen',
verbose=False)
return mesh

elif mode == 'write':


pass

def optimization_hook(pb):
cnf = pb.conf
out = []
yield pb, out

state = out[-1][1].get_state_parts()
coors = pb.domain.cmesh.coors
displ = state['u'].reshape((coors.shape[0],3))
# elongation
mcoors = coors[cnf.mnodes, 2]
mdispl = displ[cnf.mnodes, 2]
dl = (mdispl[1] - mdispl[0]) / (mcoors[1] - mcoors[0])

if hasattr(cnf, 'opt_data'):
# compute slope of the force-elongation curve
cnf.opt_data['k'] = cnf.F / dl

yield None

def get_mat(coors, mode, pb):


if mode == 'qp':
# get material data
if hasattr(pb.conf, 'opt_data'):
# from homogenization
D = pb.conf.opt_data['D_homog']
else:
# given values
D = stiffness_from_youngpoisson(3, 150.0e9, 0.3)
(continues on next page)

254 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

nqp = coors.shape[0]
return {'D': nm.tile(D, (nqp, 1, 1))}

def define(is_opt=False):
filename_mesh = UserMeshIO(mesh_hook)
mnodes = (107, 113) # nodes for elongation eval.

regions = {
'Omega': 'all',
'Bottom': ('vertices in (z < 0.001)', 'facet'),
'Top': ('vertices in (z > 0.099)', 'facet'),
}

functions = {
'get_mat': (lambda ts, coors, mode=None, problem=None, **kwargs:
get_mat(coors, mode, problem),),
}

S = 1.083500e-05 # specimen cross-section


F = 5.0e3 # force
materials = {
'solid': 'get_mat',
'load': ({'val': F / S},),
}

fields = {
'displacement': ('real', 'vector', 'Omega', 1),
}

variables = {
'u': ('unknown field', 'displacement', 0),
'v': ('test field', 'displacement', 'u'),
}

ebcs = {
'FixedBottom': ('Bottom', {'u.all': 0.0}),
'FixedTop': ('Top', {'u.0': 0.0, 'u.1': 0.0}),
}

equations = {
'balance_of_forces' :
"""dw_lin_elastic.5.Omega(solid.D, v, u)
= dw_surface_ltr.5.Top(load.val, v)""",
}

solvers = {
'ls': ('ls.scipy_direct', {}),
'newton': ('nls.newton', {'eps_a': 1e-6, 'eps_r': 1.e-6,
'check': 0, 'problem': 'nonlinear'}),
}

(continues on next page)

1.5. Examples 255


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


options = {
'parametric_hook': 'optimization_hook',
'output_dir' : 'output',
}

return locals()

homogenization/linear_homogenization.py

Description
Compute homogenized elastic coefficients for a given heterogeneous linear elastic microstructure, see [1] for details or
[2] and [3] for a quick explanation.
[1] D. Cioranescu, J.S.J. Paulin: Homogenization in open sets with holes. Journal of Mathematical Analysis and
Applications 71(2), 1979, pages 590-607. https://doi.org/10.1016/0022-247X(79)90211-7
[2] J. Pinho-da-Cruz, J.A. Oliveira, F. Teixeira-Dias: Asymptotic homogenisation in linear elasticity. Part I: Mathe-
matical formulation and finite element modelling. Computational Materials Science 45(4), 2009, pages 1073-1080.
http://dx.doi.org/10.1016/j.commatsci.2009.02.025
[3] J. Pinho-da-Cruz, J.A. Oliveira, F. Teixeira-Dias: Asymptotic homogenisation in linear elasticity. Part II: Finite
element procedures and multiscale applications. Computational Materials Science 45(4), 2009, pages 1081-1096.
http://dx.doi.org/10.1016/j.commatsci.2009.01.027
source code

r"""
Compute homogenized elastic coefficients for a given heterogeneous linear
elastic microstructure, see [1] for details or [2] and [3] for a quick
explanation.

[1] D. Cioranescu, J.S.J. Paulin: Homogenization in open sets with holes.


Journal of Mathematical Analysis and Applications 71(2), 1979, pages 590-607.
https://doi.org/10.1016/0022-247X(79)90211-7

[2] J. Pinho-da-Cruz, J.A. Oliveira, F. Teixeira-Dias:


Asymptotic homogenisation in linear elasticity.
Part I: Mathematical formulation and finite element modelling.
Computational Materials Science 45(4), 2009, pages 1073-1080.
http://dx.doi.org/10.1016/j.commatsci.2009.02.025

[3] J. Pinho-da-Cruz, J.A. Oliveira, F. Teixeira-Dias:


Asymptotic homogenisation in linear elasticity.
Part II: Finite element procedures and multiscale applications.
Computational Materials Science 45(4), 2009, pages 1081-1096.
http://dx.doi.org/10.1016/j.commatsci.2009.01.027
"""

from __future__ import absolute_import


import sfepy.discrete.fem.periodic as per
from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson
from sfepy.homogenization.utils import define_box_regions
(continues on next page)

256 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


import sfepy.homogenization.coefs_base as cb
from sfepy import data_dir
from sfepy.base.base import Struct
from sfepy.homogenization.recovery import compute_micro_u,\
compute_stress_strain_u, compute_mac_stress_part

def recovery_le(pb, corrs, macro):

out = {}

dim = corrs['corrs_le']['u_00'].shape[1]
mic_u = - compute_micro_u(corrs['corrs_le'], macro['strain'], 'u', dim)

out['u_mic'] = Struct(name='output_data',
mode='vertex', data=mic_u,
var_name='u', dofs=None)

stress_Y, strain_Y = \
compute_stress_strain_u(pb, 'i', 'Y', 'mat.D', 'u', mic_u)
stress_Y += \
compute_mac_stress_part(pb, 'i', 'Y', 'mat.D', 'u', macro['strain'])

strain = macro['strain'] + strain_Y

out['cauchy_strain'] = Struct(name='output_data',
mode='cell', data=strain,
dofs=None)
out['cauchy_stress'] = Struct(name='output_data',
mode='cell', data=stress_Y,
dofs=None)
return out

filename_mesh = data_dir + '/meshes/3d/matrix_fiber.mesh'


dim = 3
region_lbn = (0, 0, 0)
region_rtf = (1, 1, 1)

regions = {
'Y': 'all',
'Ym': 'cells of group 1',
'Yc': 'cells of group 2',
}
regions.update(define_box_regions(dim, region_lbn, region_rtf))

materials = {
'mat': ({'D': {'Ym': stiffness_from_youngpoisson(dim, 7.0e9, 0.4),
'Yc': stiffness_from_youngpoisson(dim, 70.0e9, 0.2)}},),
}

fields = {
(continues on next page)

1.5. Examples 257


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'corrector': ('real', dim, 'Y', 1),
}

variables = {
'u': ('unknown field', 'corrector', 0),
'v': ('test field', 'corrector', 'u'),
'Pi': ('parameter field', 'corrector', 'u'),
'Pi1': ('parameter field', 'corrector', '(set-to-None)'),
'Pi2': ('parameter field', 'corrector', '(set-to-None)'),
}

functions = {
'match_x_plane': (per.match_x_plane,),
'match_y_plane': (per.match_y_plane,),
'match_z_plane': (per.match_z_plane,),
}

ebcs = {
'fixed_u': ('Corners', {'u.all': 0.0}),
}

if dim == 3:
epbcs = {
'periodic_x': (['Left', 'Right'], {'u.all': 'u.all'},
'match_x_plane'),
'periodic_y': (['Near', 'Far'], {'u.all': 'u.all'},
'match_y_plane'),
'periodic_z': (['Top', 'Bottom'], {'u.all': 'u.all'},
'match_z_plane'),
}
else:
epbcs = {
'periodic_x': (['Left', 'Right'], {'u.all': 'u.all'},
'match_x_plane'),
'periodic_y': (['Bottom', 'Top'], {'u.all': 'u.all'},
'match_y_plane'),
}

all_periodic = ['periodic_%s' % ii for ii in ['x', 'y', 'z'][:dim]]

integrals = {
'i': 2,
}

options = {
'coefs': 'coefs',
'requirements': 'requirements',
'ls': 'ls', # linear solver to use
'volume': {'expression': 'ev_volume.i.Y(u)'},
'output_dir': 'output',
'coefs_filename': 'coefs_le',
'recovery_hook': 'recovery_le',
(continues on next page)

258 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


}

equation_corrs = {
'balance_of_forces':
"""dw_lin_elastic.i.Y(mat.D, v, u) =
- dw_lin_elastic.i.Y(mat.D, v, Pi)"""
}

expr_coefs = """dw_lin_elastic.i.Y(mat.D, Pi1, Pi2)"""

coefs = {
'D': {
'requires': ['pis', 'corrs_rs'],
'expression': expr_coefs,
'set_variables': [('Pi1', ('pis', 'corrs_rs'), 'u'),
('Pi2', ('pis', 'corrs_rs'), 'u')],
'class': cb.CoefSymSym,
},
'filenames': {},
}

requirements = {
'pis': {
'variables': ['u'],
'class': cb.ShapeDimDim,
'save_name': 'corrs_pis',
},
'corrs_rs': {
'requires': ['pis'],
'ebcs': ['fixed_u'],
'epbcs': all_periodic,
'equations': equation_corrs,
'set_variables': [('Pi', 'pis', 'u')],
'class': cb.CorrDimDim,
'save_name': 'corrs_le',
'is_linear': True,
},
}

solvers = {
'ls': ('ls.scipy_direct', {}),
'newton': ('nls.newton', {
'i_max': 1,
'eps_a': 1e-4,
})
}

1.5. Examples 259


SfePy Documentation, Release version: 2022.1+git.f9873484

homogenization/linear_homogenization_postproc.py

Description
This example shows how to use the VTK postprocessing functions.
source code

"""
This example shows how to use the VTK postprocessing functions.
"""

from __future__ import absolute_import


import os.path as osp
from .linear_homogenization import *
from sfepy.postprocess.utils_vtk import get_vtk_from_mesh,\
get_vtk_by_group, get_vtk_surface, get_vtk_edges, write_vtk_to_file,\
tetrahedralize_vtk_mesh

options.update({
'post_process_hook' : 'post_process',
})

def post_process(out, problem, state, extend=False):

mesh = problem.domain.mesh
mesh_name = mesh.name[mesh.name.rfind(osp.sep) + 1:]

vtkdata = get_vtk_from_mesh(mesh, out, 'postproc_')


matrix = get_vtk_by_group(vtkdata, 1, 1)

matrix_surf = get_vtk_surface(matrix)
matrix_surf_tri = tetrahedralize_vtk_mesh(matrix_surf)
write_vtk_to_file('%s_mat1_surface.vtk' % mesh_name, matrix_surf_tri)

matrix_edges = get_vtk_edges(matrix)
write_vtk_to_file('%s_mat1_edges.vtk' % mesh_name, matrix_edges)

return out

homogenization/linear_homogenization_up.py

Description
Compute homogenized elastic coefficients for a given heterogeneous linear elastic microstructure, see [1] for details or
[2] and [3] for a quick explanation. The mixed formulation, where displacements and pressures are as unknowns, is
used in this example.
[1] D. Cioranescu, J.S.J. Paulin: Homogenization in open sets with holes. Journal of Mathematical Analysis and
Applications 71(2), 1979, pages 590-607. https://doi.org/10.1016/0022-247X(79)90211-7
[2] J. Pinho-da-Cruz, J.A. Oliveira, F. Teixeira-Dias: Asymptotic homogenisation in linear elasticity. Part I: Mathe-
matical formulation and finite element modelling. Computational Materials Science 45(4), 2009, pages 1073-1080.
http://dx.doi.org/10.1016/j.commatsci.2009.02.025

260 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

[3] J. Pinho-da-Cruz, J.A. Oliveira, F. Teixeira-Dias: Asymptotic homogenisation in linear elasticity. Part II: Finite
element procedures and multiscale applications. Computational Materials Science 45(4), 2009, pages 1081-1096.
http://dx.doi.org/10.1016/j.commatsci.2009.01.027
source code

r"""
Compute homogenized elastic coefficients for a given heterogeneous linear
elastic microstructure, see [1] for details or [2] and [3] for a quick
explanation. The mixed formulation, where displacements and pressures are
as unknowns, is used in this example.

[1] D. Cioranescu, J.S.J. Paulin: Homogenization in open sets with holes.


Journal of Mathematical Analysis and Applications 71(2), 1979, pages 590-607.
https://doi.org/10.1016/0022-247X(79)90211-7

[2] J. Pinho-da-Cruz, J.A. Oliveira, F. Teixeira-Dias:


Asymptotic homogenisation in linear elasticity.
Part I: Mathematical formulation and finite element modelling.
Computational Materials Science 45(4), 2009, pages 1073-1080.
http://dx.doi.org/10.1016/j.commatsci.2009.02.025

[3] J. Pinho-da-Cruz, J.A. Oliveira, F. Teixeira-Dias:


Asymptotic homogenisation in linear elasticity.
Part II: Finite element procedures and multiscale applications.
Computational Materials Science 45(4), 2009, pages 1081-1096.
http://dx.doi.org/10.1016/j.commatsci.2009.01.027
"""

from __future__ import absolute_import


import numpy as nm

import sfepy.discrete.fem.periodic as per


from sfepy.mechanics.matcoefs import stiffness_from_youngpoisson_mixed,\
bulk_from_youngpoisson
from sfepy.homogenization.utils import define_box_regions, get_box_volume
import sfepy.homogenization.coefs_base as cb

from sfepy import data_dir


from sfepy.base.base import Struct
from sfepy.homogenization.recovery import compute_micro_u,\
compute_stress_strain_u, compute_mac_stress_part, add_stress_p

def recovery_le(pb, corrs, macro):


out = {}
dim = corrs['corrs_le']['u_00'].shape[1]
mic_u = - compute_micro_u(corrs['corrs_le'], macro['strain'], 'u', dim)
mic_p = - compute_micro_u(corrs['corrs_le'], macro['strain'], 'p', dim)

out['u_mic'] = Struct(name='output_data',
mode='vertex', data=mic_u,
var_name='u', dofs=None)
out['p_mic'] = Struct(name='output_data',
(continues on next page)

1.5. Examples 261


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


mode='cell', data=mic_p[:, nm.newaxis,
:, nm.newaxis],
var_name='p', dofs=None)

stress_Y, strain_Y = \
compute_stress_strain_u(pb, 'i', 'Y', 'mat.D', 'u', mic_u)
stress_Y += \
compute_mac_stress_part(pb, 'i', 'Y', 'mat.D', 'u', macro['strain'])
add_stress_p(stress_Y, pb, 'i', 'Y', 'p', mic_p)

strain = macro['strain'] + strain_Y

out['cauchy_strain'] = Struct(name='output_data',
mode='cell', data=strain,
dofs=None)
out['cauchy_stress'] = Struct(name='output_data',
mode='cell', data=stress_Y,
dofs=None)
return out

dim = 3
filename_mesh = data_dir + '/meshes/3d/matrix_fiber.mesh'
region_lbn = (0, 0, 0)
region_rtf = (1, 1, 1)

regions = {
'Y': 'all',
'Ym': 'cells of group 1',
'Yc': 'cells of group 2',
}
regions.update(define_box_regions(dim, region_lbn, region_rtf))

materials = {
'mat': ({'D': {'Ym': stiffness_from_youngpoisson_mixed(dim, 7.0e9, 0.4),
'Yc': stiffness_from_youngpoisson_mixed(dim, 70.0e9, 0.2)},
'gamma': {'Ym': 1.0/bulk_from_youngpoisson(7.0e9, 0.4),
'Yc': 1.0/bulk_from_youngpoisson(70.0e9, 0.2)}},),
}

fields = {
'corrector_u': ('real', dim, 'Y', 1),
'corrector_p': ('real', 1, 'Y', 0),
}

variables = {
'u': ('unknown field', 'corrector_u'),
'v': ('test field', 'corrector_u', 'u'),
'p': ('unknown field', 'corrector_p'),
'q': ('test field', 'corrector_p', 'p'),
'Pi': ('parameter field', 'corrector_u', 'u'),
(continues on next page)

262 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'Pi1u': ('parameter field', 'corrector_u', '(set-to-None)'),
'Pi2u': ('parameter field', 'corrector_u', '(set-to-None)'),
'Pi1p': ('parameter field', 'corrector_p', '(set-to-None)'),
'Pi2p': ('parameter field', 'corrector_p', '(set-to-None)'),
}

functions = {
'match_x_plane': (per.match_x_plane,),
'match_y_plane': (per.match_y_plane,),
'match_z_plane': (per.match_z_plane,),
}

ebcs = {
'fixed_u': ('Corners', {'u.all': 0.0}),
}

if dim == 3:
epbcs = {
'periodic_x': (['Left', 'Right'], {'u.all': 'u.all'},
'match_x_plane'),
'periodic_y': (['Near', 'Far'], {'u.all': 'u.all'},
'match_y_plane'),
'periodic_z': (['Top', 'Bottom'], {'u.all': 'u.all'},
'match_z_plane'),
}
else:
epbcs = {
'periodic_x': (['Left', 'Right'], {'u.all': 'u.all'},
'match_x_plane'),
'periodic_y': (['Bottom', 'Top'], {'u.all': 'u.all'},
'match_y_plane'),
}

all_periodic = ['periodic_%s' % ii for ii in ['x', 'y', 'z'][:dim]]

integrals = {
'i': 2,
}

options = {
'coefs': 'coefs',
'requirements': 'requirements',
'ls': 'ls', # linear solver to use
'volume': {'value': get_box_volume(dim, region_lbn, region_rtf), },
'output_dir': 'output',
'coefs_filename': 'coefs_le_up',
'recovery_hook': 'recovery_le',
'multiprocessing': False,
}

equation_corrs = {
'balance_of_forces':
(continues on next page)

1.5. Examples 263


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


""" dw_lin_elastic.i.Y(mat.D, v, u)
- dw_stokes.i.Y(v, p) =
- dw_lin_elastic.i.Y(mat.D, v, Pi)""",
'pressure constraint':
"""- dw_stokes.i.Y(u, q)
- dw_dot.i.Y(mat.gamma, q, p) =
+ dw_stokes.i.Y(Pi, q)""",
}

coefs = {
'elastic_u': {
'requires': ['pis', 'corrs_rs'],
'expression': 'dw_lin_elastic.i.Y(mat.D, Pi1u, Pi2u)',
'set_variables': [('Pi1u', ('pis', 'corrs_rs'), 'u'),
('Pi2u', ('pis', 'corrs_rs'), 'u')],
'class': cb.CoefSymSym,
},
'elastic_p': {
'requires': ['corrs_rs'],
'expression': 'dw_dot.i.Y(mat.gamma, Pi1p, Pi2p)',
'set_variables': [('Pi1p', 'corrs_rs', 'p'),
('Pi2p', 'corrs_rs', 'p')],
'class': cb.CoefSymSym,
},
'D': {
'requires': ['c.elastic_u', 'c.elastic_p'],
'class': cb.CoefSum,
},
'filenames': {},
}

requirements = {
'pis': {
'variables': ['u'],
'class': cb.ShapeDimDim,
},
'corrs_rs': {
'requires': ['pis'],
'ebcs': ['fixed_u'],
'epbcs': all_periodic,
'equations': equation_corrs,
'set_variables': [('Pi', 'pis', 'u')],
'class': cb.CorrDimDim,
'save_name': 'corrs_le',
'is_linear': True,
},
}

solvers = {
'ls': ('ls.auto_iterative', {}),
'newton': ('nls.newton', {
'i_max': 1,
(continues on next page)

264 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'eps_a': 1e2,
})
}

homogenization/material_opt.py

Description
See the Material Identification tutorial for a comprehensive description of this example.
source code
#!/usr/bin/env python
"""
See the :ref:`sec-mat_optim` tutorial for a comprehensive description of this
example.
"""
from __future__ import print_function
from __future__ import absolute_import
import sys
sys.path.append('.')

import numpy as nm
from scipy.optimize import fmin_tnc

import sfepy
from sfepy.base.base import Struct
from sfepy.base.log import Log

class MaterialOptimizer(object):

@staticmethod
def create_app(filename, is_homog=False, **kwargs):
from sfepy.base.conf import ProblemConf, get_standard_keywords
from sfepy.homogenization.homogen_app import HomogenizationApp
from sfepy.applications import PDESolverApp

required, other = get_standard_keywords()


if is_homog:
required.remove('equations')

conf = ProblemConf.from_file(filename, required, other,


define_args=kwargs)
options = Struct(output_filename_trunk=None,
save_ebc=False,
save_ebc_nodes=False,
save_regions=False,
save_regions_as_groups=False,
save_field_meshes=False,
solve_not=False)

if is_homog:
(continues on next page)

1.5. Examples 265


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


app = HomogenizationApp(conf, options, 'material_opt_micro:')

else:
app = PDESolverApp(conf, options, 'material_opt_macro:')

app.conf.opt_data = {}
opts = conf.options
if hasattr(opts, 'parametric_hook'): # Parametric study.
parametric_hook = conf.get_function(opts.parametric_hook)
app.parametrize(parametric_hook)

return app

def x_norm2real(self, x):


return x * (self.x_U - self.x_L) + self.x_L

def x_real2norm(self, x):


return (x - self.x_L) / (self.x_U - self.x_L)

def __init__(self, macro_fn, micro_fn, x0, x_L, x_U, exp_data):


self.macro_app = self.create_app(macro_fn, is_homog=False, is_opt=True)
self.micro_app = self.create_app(micro_fn, is_homog=True, is_opt=True)
self.x_L = nm.array(x_L)
self.x_U = nm.array(x_U)
self.x0 = self.x_real2norm(nm.array(x0))
self.x = []
self.eval_f = []
self.exp_data = exp_data

@staticmethod
def rotate_mat(D, angle):
s = nm.sin(angle)
c = nm.cos(angle)
s2 = s**2
c2 = c**2
sc = s * c
T = nm.array([[c2, 0, s2, 0, 2*sc,0],
[0, 1, 0, 0, 0, 0],
[s2, 0, c2, 0, -2*sc, 0],
[0, 0, 0, c, 0, -s],
[-sc, 0, sc, 0, c2 - s2, 0],
[0, 0, 0, s, 0, c]])

return nm.dot(nm.dot(T, D), T.T)

def matopt_eval(self, x):


mic_od = self.micro_app.conf.opt_data
mac_od = self.macro_app.conf.opt_data

mic_od['coefs'] = {}
mic_od['mat_params'] = x
self.micro_app()
(continues on next page)

266 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

D = mic_od['D_homog']
val = 0.0
aux = []
for phi, exp_k in self.exp_data:
print('phi = %d' % phi)

mac_od['D_homog'] = self.rotate_mat(D, nm.deg2rad(phi))


self.macro_app()

comp_k = mac_od['k']
val += (1.0 - comp_k / exp_k)**2
aux.append((comp_k, exp_k))

val = nm.sqrt(val)
self.x.append(x)
self.eval_f.append(val)

return val

def iter_step(self, x, first_step=False):


if first_step:
self.log = Log([['O'], ['E_f', 'E_m'], ['v_f', 'v_m']],
ylabels=['Obj. fun.', "Young's modulus", "Poisson's ratio"],
xlabels=['iter', 'iter', 'iter'],
aggregate=0)
self.istep = 0
self.log(0.5, x[0], x[2], x[1], x[3],
x=[0, 0, 0, 0])
else:
self.istep += 1
self.log(self.eval_f[-1], x[0], x[2], x[1], x[3],
x=(self.istep,)*4)

def material_optimize(self):
x0 = self.x0
bnds = zip(self.x_real2norm(self.x_L), self.x_real2norm(self.x_U))
feval = lambda x: self.matopt_eval(self.x_norm2real(x))
istep = lambda x: self.iter_step(self.x_norm2real(x))
self.iter_step(self.x_norm2real(x0), first_step=True)

print('>>> material optimization START <<<')


xopt = fmin_tnc(feval, x0, approx_grad=True, bounds=list(bnds),
xtol=1e-3, callback=istep)
print('>>> material optimization FINISHED <<<')

self.log(finished=True)
return self.x_norm2real(xopt[0])

def main():
srcdir = sfepy.base_dir + '/examples/homogenization/'
micro_filename = srcdir + 'homogenization_opt.py'
(continues on next page)

1.5. Examples 267


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


macro_filename = srcdir + 'linear_elasticity_opt.py'

exp_data = zip([0, 30, 60, 90], [1051140., 197330., 101226., 95474.])


mo = MaterialOptimizer(macro_filename, micro_filename,
[160.e9, 0.25, 5.e9, 0.45],
[120e9, 0.2, 2e9, 0.2],
[200e9, 0.45, 8e9, 0.45],
list(exp_data))

optim_par = mo.material_optimize()
print('optimized parameters: ', optim_par)

if __name__ == '__main__':
main()

homogenization/nonlinear_homogenization.py

Description
missing description!
source code
# -*- coding: utf-8 -*-
import numpy as nm
from sfepy.homogenization.utils import define_box_regions
import sfepy.homogenization.coefs_base as cb
import sfepy.discrete.fem.periodic as per
from sfepy.base.base import Struct
from sfepy.terms.terms_hyperelastic_ul import\
HyperElasticULFamilyData, NeoHookeanULTerm, BulkPenaltyULTerm
from sfepy.terms.extmods.terms import sym2nonsym
from sfepy.discrete.functions import ConstantFunctionByRegion
from sfepy import data_dir
import sfepy.linalg as la

def recovery_hook(pb, ncoors, region, ts,


naming_scheme='step_iel', recovery_file_tag=''):
from sfepy.base.ioutils import get_print_info
from sfepy.homogenization.recovery import get_output_suffix
import os.path as op

for ii, icell in enumerate(region.cells):


out = {}
pb.set_mesh_coors(ncoors[ii], update_fields=True,
clear_all=False, actual=True)
stress = pb.evaluate('ev_integrate_mat.3.Y(mat_he.S, u)',
mode='el_avg')

out['cauchy_stress'] = Struct(name='output_data',
mode='cell',
(continues on next page)

268 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


data=stress,
dofs=None)

strain = pb.evaluate('ev_integrate_mat.3.Y(mat_he.E, u)',


mode='el_avg')

out['green_strain'] = Struct(name='output_data',
mode='cell',
data=strain,
dofs=None)

out['displacement'] = Struct(name='output_data',
mode='vertex',
data=ncoors[ii] - pb.get_mesh_coors(),
dofs=None)

output_dir = pb.conf.options.get('output_dir', '.')


format = get_print_info(pb.domain.mesh.n_el, fill='0')[1]
suffix = get_output_suffix(icell, ts, naming_scheme, format,
pb.output_format)

micro_name = pb.get_output_name(extra='recovered_'
+ recovery_file_tag + suffix)
filename = op.join(output_dir, op.basename(micro_name))
fpv = pb.conf.options.get('file_per_var', False)
pb.save_state(filename, out=out, file_per_var=fpv)

def def_mat(ts, mode, coors, term, pb):


if not (mode == 'qp'):
return

if not hasattr(pb, 'family_data'):


pb.family_data = HyperElasticULFamilyData()

update_var = pb.conf.options.mesh_update_variable
if pb.equations is None:
state_u = pb.create_variables([update_var])[update_var]
else:
state_u = pb.get_variables()[update_var]

if state_u.data[0] is None:
state_u.init_data()

state_u.set_data(
pb.domain.get_mesh_coors(actual=True) - pb.domain.get_mesh_coors())
state_u.field.clear_mappings()
family_data = pb.family_data(state_u, term.region,
term.integral, term.integration)

if len(state_u.field.mappings0) == 0:
state_u.field.save_mappings()
(continues on next page)

1.5. Examples 269


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

n_el, n_qp, dim, n_en, n_c = state_u.get_data_shape(term.integral,


term.integration,
term.region.name)

conf_mat = pb.conf.materials
solid_key = [key for key in conf_mat.keys() if 'solid' in key][0]
solid_mat = conf_mat[solid_key].values
mat = {}
for mat_key in ['mu', 'K']:
if isinstance(solid_mat[mat_key], dict):
mat_fun = ConstantFunctionByRegion({mat_key: solid_mat[mat_key]})
mat[mat_key] = mat_fun.function(ts=ts, coors=coors, mode='qp',
term=term, problem=pb)[mat_key].reshape((n_el, n_qp, 1, 1))
else:
mat[mat_key] = nm.ones((n_el, n_qp, 1, 1)) * solid_mat[mat_key]

shape = family_data.green_strain.shape[:2]
sym = family_data.green_strain.shape[-2]
dim2 = dim**2

fargs = [family_data.get(name)
for name in NeoHookeanULTerm.family_data_names]
stress = nm.empty(shape + (sym, 1), dtype=nm.float64)
tanmod = nm.empty(shape + (sym, sym), dtype=nm.float64)
NeoHookeanULTerm.stress_function(stress, mat['mu'], *fargs)
NeoHookeanULTerm.tan_mod_function(tanmod, mat['mu'], *fargs)

fargs = [family_data.get(name)
for name in BulkPenaltyULTerm.family_data_names]
stress_p = nm.empty(shape + (sym, 1), dtype=nm.float64)
tanmod_p = nm.empty(shape + (sym, sym), dtype=nm.float64)
BulkPenaltyULTerm.stress_function(stress_p, mat['K'], *fargs)
BulkPenaltyULTerm.tan_mod_function(tanmod_p, mat['K'], *fargs)

stress_ns = nm.zeros(shape + (dim2, dim2), dtype=nm.float64)


tanmod_ns = nm.zeros(shape + (dim2, dim2), dtype=nm.float64)
sym2nonsym(stress_ns, stress + stress_p)
sym2nonsym(tanmod_ns, tanmod + tanmod_p)

npts = nm.prod(shape)
J = family_data.det_f
mtx_f = family_data.mtx_f.reshape((npts, dim, dim))

out = {
'E': 0.5 * (la.dot_sequences(mtx_f, mtx_f, 'ATB') - nm.eye(dim)),
'A': ((tanmod_ns + stress_ns) / J).reshape((npts, dim2, dim2)),
'S': ((stress + stress_p) / J).reshape((npts, sym, 1)),
}

return out

(continues on next page)

270 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

filename_mesh = data_dir + '/meshes/2d/special/circle_in_square_small.mesh'


dim = 2

options = {
'coefs': 'coefs',
'requirements': 'requirements',
'volume': {'expression': 'ev_volume.5.Y(u)'},
'output_dir': './output',
'coefs_filename': 'coefs_hyper_homog',
'multiprocessing': True,
'chunks_per_worker': 2,
'micro_update': {'coors': [('corrs_rs', 'u', 'mtx_e')]},
'mesh_update_variable': 'u',
'recovery_hook': 'recovery_hook',
'store_micro_idxs': [49, 81],
}

fields = {
'displacement': ('real', 'vector', 'Y', 1),
}

functions = {
'match_x_plane': (per.match_x_plane,),
'match_y_plane': (per.match_y_plane,),
'mat_fce': (lambda ts, coors, mode=None, term=None, problem=None, **kwargs:
def_mat(ts, mode, coors, term, problem),),
}

materials = {
'mat_he': 'mat_fce',
'solid': ({'K': 1000,
'mu': {'Ym': 100, 'Yc': 10},
},),
}

variables = {
'u': ('unknown field', 'displacement'),
'v': ('test field', 'displacement', 'u'),
'Pi': ('parameter field', 'displacement', 'u'),
'Pi1u': ('parameter field', 'displacement', '(set-to-None)'),
'Pi2u': ('parameter field', 'displacement', '(set-to-None)'),
}

regions = {
'Y': 'all',
'Ym': 'cells of group 1',
'Yc': 'cells of group 2',
}

regions.update(define_box_regions(dim, (0., 0.), (1., 1.)))

(continues on next page)

1.5. Examples 271


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


ebcs = {
'fixed_u': ('Corners', {'u.all': 0.0}),
}

epbcs = {
'periodic_ux': (['Left', 'Right'], {'u.all': 'u.all'}, 'match_x_plane'),
'periodic_uy': (['Bottom', 'Top'], {'u.all': 'u.all'}, 'match_y_plane'),
}

coefs = {
'A': {
'requires': ['pis', 'corrs_rs'],
'expression': 'dw_nonsym_elastic.3.Y(mat_he.A, Pi1u, Pi2u)',
'set_variables': [('Pi1u', ('pis', 'corrs_rs'), 'u'),
('Pi2u', ('pis', 'corrs_rs'), 'u')],
'class': cb.CoefNonSymNonSym,
},
'S': {
'expression': 'ev_integrate_mat.3.Y(mat_he.S, u)',
'class': cb.CoefOne,
}
}

requirements = {
'pis': {
'variables': ['u'],
'class': cb.ShapeDimDim,
},
'corrs_rs': {
'requires': ['pis'],
'ebcs': ['fixed_u'],
'epbcs': ['periodic_ux', 'periodic_uy'],
'equations': {
'balance_of_forces':
"""dw_nonsym_elastic.3.Y(mat_he.A, v, u)
= - dw_nonsym_elastic.3.Y(mat_he.A, v, Pi)"""
},
'set_variables': [('Pi', 'pis', 'u')],
'class': cb.CorrDimDim,
'save_name': 'corrs_hyper_homog',
},
}

solvers = {
'ls': ('ls.scipy_direct', {}),
'newton': ('nls.newton', {
'i_max': 1,
'eps_a': 1e-4,
'problem': 'nonlinear',
}),
}

272 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

homogenization/nonlinear_hyperelastic_mM.py

Description
missing description!

source code

import numpy as nm
import six

from sfepy import data_dir


from sfepy.base.base import Struct, output
from sfepy.terms.terms_hyperelastic_ul import HyperElasticULFamilyData
from sfepy.homogenization.micmac import get_homog_coefs_nonlinear
import sfepy.linalg as la
from sfepy.discrete.evaluate import Evaluator

hyperelastic_data = {}

def post_process(out, pb, state, extend=False):


if isinstance(state, dict):
pass
(continues on next page)

1.5. Examples 273


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


else:
pb.update_materials_flag = 2
stress = pb.evaluate('ev_integrate_mat.1.Omega(solid.S, u)',
mode='el_avg')

out['cauchy_stress'] = Struct(name='output_data',
mode='cell',
data=stress,
dofs=None)

strain = pb.evaluate('ev_integrate_mat.1.Omega(solid.E, u)',


mode='el_avg')

out['green_strain'] = Struct(name='output_data',
mode='cell',
data=strain,
dofs=None)

pb.update_materials_flag = 0

if pb.conf.options.get('recover_micro', False):
happ = pb.homogen_app
if pb.ts.step == 0:
rname = pb.conf.options.recovery_region
rcells = pb.domain.regions[rname].get_cells()
sh = hyperelastic_data['homog_mat_shape']

happ.app_options.store_micro_idxs = sh[1] * rcells


else:
hpb = happ.problem
recovery_hook = hpb.conf.options.get('recovery_hook', None)
if recovery_hook is not None:
recovery_hook = hpb.conf.get_function(recovery_hook)
rname = pb.conf.options.recovery_region
rcoors = []
for ii in happ.app_options.store_micro_idxs:
key = happ.get_micro_cache_key('coors', ii, pb.ts.step)
if key in happ.micro_state_cache:
rcoors.append(happ.micro_state_cache[key])

recovery_hook(hpb, rcoors, pb.domain.regions[rname], pb.ts)

return out

def get_homog_mat(ts, coors, mode, term=None, problem=None, **kwargs):


if problem.update_materials_flag == 2 and mode == 'qp':
out = hyperelastic_data['homog_mat']
return {k: nm.array(v) for k, v in six.iteritems(out)}
elif problem.update_materials_flag == 0 or not mode == 'qp':
return

(continues on next page)

274 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


output('get_homog_mat')
dim = problem.domain.mesh.dim

update_var = problem.conf.options.mesh_update_variables[0]
state_u = problem.equations.variables[update_var]
state_u.field.clear_mappings()
family_data = problem.family_data(state_u, term.region,
term.integral, term.integration)

mtx_f = family_data.mtx_f.reshape((coors.shape[0],)
+ family_data.mtx_f.shape[-2:])

if hasattr(problem, 'mtx_f_prev'):
rel_mtx_f = la.dot_sequences(mtx_f, nm.linalg.inv(problem.mtx_f_prev),
'AB')
else:
rel_mtx_f = mtx_f

problem.mtx_f_prev = mtx_f.copy()

macro_data = {'mtx_e': rel_mtx_f - nm.eye(dim)} # '*' - macro strain


out = get_homog_coefs_nonlinear(ts, coors, mode, macro_data,
term=term, problem=problem,
iteration=problem.iiter, **kwargs)

out['E'] = 0.5 * (la.dot_sequences(mtx_f, mtx_f, 'ATB') - nm.eye(dim))

hyperelastic_data['time'] = ts.step
hyperelastic_data['homog_mat_shape'] = family_data.det_f.shape[:2]
hyperelastic_data['homog_mat'] = \
{k: nm.array(v) for k, v in six.iteritems(out)}

return out

def ulf_iteration_hook(pb, nls, vec, it, err, err0):


Evaluator.new_ulf_iteration(pb, nls, vec, it, err, err0)

pb.iiter = it
pb.update_materials_flag = True
pb.update_materials()
pb.update_materials_flag = False

class MyEvaluator(Evaluator):
def eval_residual(self, vec, is_full=False):
if not is_full:
vec = self.problem.equations.make_full_vec(vec)
vec_r = self.problem.equations.eval_residuals(vec * 0)

return vec_r

(continues on next page)

1.5. Examples 275


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

def ulf_init(pb):
pb.family_data = HyperElasticULFamilyData()
pb_vars = pb.get_variables()
pb_vars['u'].init_data()

pb.update_materials_flag = True
pb.iiter = 0

options = {
'output_dir': 'output',
'mesh_update_variables': ['u'],
'nls_iter_hook': ulf_iteration_hook,
'pre_process_hook': ulf_init,
'micro_filename': 'examples/homogenization/nonlinear_homogenization.py',
'recover_micro': True,
'recovery_region': 'Recovery',
'post_process_hook': post_process,
'user_evaluator': MyEvaluator,
}

materials = {
'solid': 'get_homog',
}

fields = {
'displacement': ('real', 'vector', 'Omega', 1),
}

variables = {
'u': ('unknown field', 'displacement'),
'v': ('test field', 'displacement', 'u'),
}

filename_mesh = data_dir + '/meshes/2d/its2D.mesh'

regions = {
'Omega': 'all',
'Left': ('vertices in (x < 0.001)', 'facet'),
'Bottom': ('vertices in (y < 0.001 )', 'facet'),
'Recovery': ('cell 49, 81', 'cell'),
}

ebcs = {
'l': ('Left', {'u.all': 0.0}),
'b': ('Bottom', {'u.all': 'move_bottom'}),
}

centre = nm.array([0, 0], dtype=nm.float64)

(continues on next page)

276 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

def move_bottom(ts, coor, **kwargs):


from sfepy.linalg import rotation_matrix2d

vec = coor[:, 0:2] - centre


angle = 3 * ts.step
print('angle:', angle)
mtx = rotation_matrix2d(angle)
out = nm.dot(vec, mtx) - vec

return out

functions = {
'move_bottom': (move_bottom,),
'get_homog': (get_homog_mat,),
}

equations = {
'balance_of_forces':
"""dw_nonsym_elastic.1.Omega(solid.A, v, u)
= - dw_lin_prestress.1.Omega(solid.S, v)""",
}

solvers = {
'ls': ('ls.scipy_direct', {}),
'newton': ('nls.newton', {
'eps_a': 1e-3,
'eps_r': 1e-3,
'i_max': 20,
}),
'ts': ('ts.simple', {
't0': 0,
't1': 1,
'n_step': 3 + 1,
'verbose': 1,
})
}

homogenization/perfusion_micro.py

Description
Homogenization of the Darcy flow in a thin porous layer. The reference cell is composed of the matrix representing
the dual porosity and of two disconnected channels representing the primary porosity, see paper [1].
[1] E. Rohan, V. LukeΕ‘: Modeling Tissue Perfusion Using a Homogenized Model with Layer-wise Decomposition.
IFAC Proceedings Volumes 45(2), 2012, pages 1029-1034. https://doi.org/10.3182/20120215-3-AT-3016.00182
source code

1.5. Examples 277


SfePy Documentation, Release version: 2022.1+git.f9873484

# -*- coding: utf-8


r"""
Homogenization of the Darcy flow in a thin porous layer.
The reference cell is composed of the matrix representing the dual porosity
and of two disconnected channels representing the primary porosity,
see paper [1].

[1] E. Rohan, V. LukeΕ‘: Modeling Tissue Perfusion Using a Homogenized


Model with Layer-wise Decomposition. IFAC Proceedings Volumes 45(2), 2012,
pages 1029-1034.
https://doi.org/10.3182/20120215-3-AT-3016.00182
"""

from __future__ import absolute_import


from sfepy.discrete.fem.periodic import match_x_plane, match_y_plane
import sfepy.homogenization.coefs_base as cb
import numpy as nm
from sfepy import data_dir
import six
from six.moves import range

def get_mats(pk, ph, pe, dim):


m1 = nm.eye(dim, dtype=nm.float64) * pk
m1[-1, -1] = pk / ph
m2 = nm.eye(dim, dtype=nm.float64) * pk
m2[-1, -1] = pk / ph ** 2

return m1, m2

def recovery_perf(pb, corrs, macro):


from sfepy.homogenization.recovery import compute_p_from_macro
from sfepy.base.base import Struct

slev = ''

micro_nnod = pb.domain.mesh.n_nod

centre_Y = nm.sum(pb.domain.mesh.coors, axis=0) / micro_nnod


nodes_Y = {}

channels = {}
for k in six.iterkeys(macro):
if 'press' in k:
channels[k[-1]] = 1

channels = list(channels.keys())

varnames = ['pM']
for ch in channels:
nodes_Y[ch] = pb.domain.regions['Y' + ch].vertices
varnames.append('p' + ch)

(continues on next page)

278 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


pvars = pb.create_variables(varnames)

press = {}

# matrix
press['M'] = \
corrs['corrs_%s_gamma_p' % pb_def['name']]['pM'] * macro['g_p'] + \
corrs['corrs_%s_gamma_m' % pb_def['name']]['pM'] * macro['g_m']

out = {}
# channels
for ch in channels:
press_mac = macro['press' + ch][0, 0]
press_mac_grad = macro['pressg' + ch]
nnod = corrs['corrs_%s_pi%s' % (pb_def['name'], ch)]\
['p%s_0' % ch].shape[0]

press_mic = nm.zeros((nnod, 1))


for key, val in \
six.iteritems(corrs['corrs_%s_pi%s' % (pb_def['name'], ch)]):
kk = int(key[-1])
press_mic += val * press_mac_grad[kk, 0]

for key in six.iterkeys(corrs):


if ('_gamma_' + ch in key):
kk = int(key[-1]) - 1
press_mic += corrs[key]['p' + ch] * macro['g' + ch][kk]

press_mic += \
compute_p_from_macro(press_mac_grad[nm.newaxis,nm.newaxis, :, :],
micro_coors[nodes_Y[ch]], 0,
centre=centre_Y, extdim=-1).reshape((nnod, 1))

press[ch] = press_mac + eps0 * press_mic

out[slev + 'p' + ch] = Struct(name='output_data',


mode='vertex',
data=press[ch],
var_name='p' + ch,
dofs=None)

pvars['p' + ch].set_data(press_mic)
dvel = pb.evaluate('ev_diffusion_velocity.iV.Y%s(mat1%s.k, p%s)'
% (ch, ch, ch),
var_dict={'p' + ch: pvars['p' + ch]},
mode='el_avg')

out[slev + 'w' + ch] = Struct(name='output_data',


mode='cell',
data=dvel,
var_name='w' + ch,
dofs=None)
(continues on next page)

1.5. Examples 279


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

press['M'] += corrs['corrs_%s_eta%s' % (pb_def['name'], ch)]['pM']\


* press_mac

pvars['pM'].set_data(press['M'])
dvel = pb.evaluate('%e * ev_diffusion_velocity.iV.YM(mat1M.k, pM)' % eps0,
var_dict={'pM': pvars['pM']}, mode='el_avg')

out[slev + 'pM'] = Struct(name='output_data',


mode='vertex',
dat=press['M'],
var_name='pM',
dofs=None)

out[slev + 'wM'] = Struct(name='output_data',


mode='cell',
data=dvel,
var_name='wM',
dofs=None)

return out

geoms = {
'2_4': ['2_4_Q1', '2', 5],
'3_8': ['3_8_Q1', '4', 5],
'3_4': ['3_4_P1', '3', 3],
}

pb_def = {
'name': '3d_2ch',
'mesh_filename': data_dir + '/meshes/3d/perfusion_micro3d.mesh',
'dim': 3,
'geom': geoms['3_4'],
'eps0': 1.0e-2,
'param_h': 1.0,
'param_kappa_m': 0.1,
'matrix_mat_el_grp': 3,
'channels': {
'A': {
'mat_el_grp': 1,
'fix_nd_grp': (4, 1),
'io_nd_grp': [1, 2, 3],
'param_kappa_ch': 1.0,
},
'B': {
'mat_el_grp': 2,
'fix_nd_grp': (14, 11),
'io_nd_grp': [11, 12, 13],
'param_kappa_ch': 2.0,
},
},
(continues on next page)

280 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


}

filename_mesh = pb_def['mesh_filename']
eps0 = pb_def['eps0']
param_h = pb_def['param_h']

# integrals
integrals = {
'iV': 2,
'iS': 2,
}

functions = {
'match_x_plane': (match_x_plane,),
'match_y_plane': (match_y_plane,),
}

aux = []
for ch, val in six.iteritems(pb_def['channels']):
aux.append('r.bYM' + ch)

# basic regions
regions = {
'Y': 'all',
'YM': 'cells of group %d' % pb_def['matrix_mat_el_grp'],
# periodic boundaries
'Pl': ('vertices in (x < 0.001)', 'facet'),
'Pr': ('vertices in (x > 0.999)', 'facet'),
'PlYM': ('r.Pl *v r.YM', 'facet'),
'PrYM': ('r.Pr *v r.YM', 'facet'),
'bYMp': ('r.bYp *v r.YM', 'facet', 'YM'),
'bYMm': ('r.bYm *v r.YM', 'facet', 'YM'),
'bYMpm': ('r.bYMp +v r.bYMm', 'facet', 'YM'),
}

# matrix/channel boundaries
regions.update({
'bYMchs': (' +v '.join(aux), 'facet', 'YM'),
'YMmchs': 'r.YM -v r.bYMchs',
})

# boundary conditions Gamma+/-


ebcs = {
'gamma_pm_bYMchs': ('bYMchs', {'pM.0': 0.0}),
'gamma_pm_YMmchs': ('YMmchs', {'pM.0': 1.0}),
}

# periodic boundary conditions - matrix, X-direction


epbcs = {'periodic_xYM': (['PlYM', 'PrYM'], {'pM.0': 'pM.0'}, 'match_x_plane')}
lcbcs = {}

all_periodicYM = ['periodic_%sYM' % ii for ii in ['x', 'y'][:pb_def['dim']-1]]


(continues on next page)

1.5. Examples 281


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


all_periodicY = {}

if pb_def['dim'] == 2:
regions.update({
'bYm': ('vertices in (y < 0.001)', 'facet'),
'bYp': ('vertices in (y > 0.999)', 'facet'),
})
if pb_def['dim'] == 3:
regions.update({
'Pn': ('vertices in (y < 0.001)', 'facet'),
'Pf': ('vertices in (y > 0.999)', 'facet'),
'PnYM': ('r.Pn *v r.YM', 'facet'),
'PfYM': ('r.Pf *v r.YM', 'facet'),
'bYm': ('vertices in (z < 0.001)', 'facet'),
'bYp': ('vertices in (z > 0.999)', 'facet'),
})
# periodic boundary conditions - matrix, Y-direction
epbcs.update({
'periodic_yYM': (['PnYM', 'PfYM'], {'pM.0': 'pM.0'}, 'match_y_plane'),
})

reg_io = {}
ebcs_eta = {}
ebcs_gamma = {}

# generate regions, ebcs, epbcs


for ch, val in six.iteritems(pb_def['channels']):

all_periodicY[ch] = ['periodic_%sY%s' % (ii, ch)


for ii in ['x', 'y'][:pb_def['dim']-1]]

# channels: YA, fixedYA, bYMA (matrix/channel boundaries)


regions.update({
'Y' + ch: 'cells of group %d' % val['mat_el_grp'],
'bYM' + ch: ('r.YM *v r.Y' + ch, 'facet', 'YM'),
'PlY' + ch: ('r.Pl *v r.Y' + ch, 'facet'),
'PrY' + ch: ('r.Pr *v r.Y' + ch, 'facet'),
})

if 'fix_nd_grp' in val:
regions.update({
'fixedY' + ch: ('vertices of group %d' % val['fix_nd_grp'][0],
'vertex'),
})

ebcs_eta[ch] = []
for ch2, val2 in six.iteritems(pb_def['channels']):
aux = 'eta%s_bYM%s' % (ch, ch2)
if ch2 == ch:
ebcs.update({aux: ('bYM' + ch2, {'pM.0': 1.0})})
else:
ebcs.update({aux: ('bYM' + ch2, {'pM.0': 0.0})})
(continues on next page)

282 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

ebcs_eta[ch].append(aux)

# boundary conditions
# periodic boundary conditions - channels, X-direction
epbcs.update({
'periodic_xY' + ch: (['PlY' + ch, 'PrY' + ch],
{'p%s.0' % ch: 'p%s.0' % ch},
'match_x_plane'),
})

if pb_def['dim'] == 3:
regions.update({
'PnY' + ch: ('r.Pn *v r.Y' + ch, 'facet'),
'PfY' + ch: ('r.Pf *v r.Y' + ch, 'facet'),
})
# periodic boundary conditions - channels, Y-direction
epbcs.update({
'periodic_yY' + ch: (['PnY' + ch, 'PfY' + ch],
{'p%s.0' % ch: 'p%s.0' % ch},
'match_y_plane'),
})

reg_io[ch] = []
aux_bY = []
# channel: inputs/outputs
for i_io in range(len(val['io_nd_grp'])):
io = '%s_%d' % (ch, i_io+1)

# regions
aux = val['io_nd_grp'][i_io]
if 'fix_nd_grp' in val and val['fix_nd_grp'][1] == aux:
regions.update({
'bY%s' % io: ('vertices of group %d +v r.fixedY%s' % (aux, ch),
'facet', 'Y%s' % ch),
})
else:
regions.update({
'bY%s' % io: ('vertices of group %d' % aux,
'facet', 'Y%s' % ch),
})

aux_bY.append('r.bY%s' % io)
reg_io[ch].append('bY%s' % io)

regions.update({
'bY' + ch: (' +v '.join(aux_bY), 'facet', 'Y' + ch),
})

# channel: inputs/outputs
for i_io in range(len(val['io_nd_grp'])):
io = '%s_%d' % (ch, i_io + 1)
(continues on next page)

1.5. Examples 283


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


ion = '%s_n%d' % (ch, i_io + 1)
regions.update({
'bY%s' % ion: ('r.bY%s -v r.bY%s' % (ch, io), 'facet', 'Y%s' % ch),
})

# boundary conditions
aux = 'fix_p%s_bY%s' % (ch, ion)
ebcs.update({
aux: ('bY%s' % ion, {'p%s.0' % ch: 0.0}),
})

lcbcs.update({
'imv' + ch: ('Y' + ch, {'ls%s.all' % ch: None}, None,
'integral_mean_value')
})

matk1, matk2 = get_mats(pb_def['param_kappa_m'], param_h, eps0, pb_def['dim'])

materials = {
'mat1M': ({'k': matk1},),
'mat2M': ({'k': matk2},),
}

fields = {
'corrector_M': ('real', 'scalar', 'YM', 1),
'vel_M': ('real', 'vector', 'YM', 1),
'vol_all': ('real', 'scalar', 'Y', 1),
}

variables = {
'pM': ('unknown field', 'corrector_M'),
'qM': ('test field', 'corrector_M', 'pM'),
'Pi_M': ('parameter field', 'corrector_M', '(set-to-None)'),
'corr_M': ('parameter field', 'corrector_M', '(set-to-None)'),
'corr1_M': ('parameter field', 'corrector_M', '(set-to-None)'),
'corr2_M': ('parameter field', 'corrector_M', '(set-to-None)'),
'wM': ('parameter field', 'vel_M', '(set-to-None)'),
'vol_all': ('parameter field', 'vol_all', '(set-to-None)'),
}

# generate regions for channel inputs/outputs


for ch, val in six.iteritems(pb_def['channels']):

matk1, matk2 = get_mats(val['param_kappa_ch'], param_h,


eps0, pb_def['dim'])
materials.update({
'mat1' + ch: ({'k': matk1},),
'mat2' + ch: ({'k': matk2},),
})

fields.update({
(continues on next page)

284 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'corrector_' + ch: ('real', 'scalar', 'Y' + ch, 1),
'vel_' + ch: ('real', 'vector', 'Y' + ch, 1),
})

variables.update({
'p' + ch: ('unknown field', 'corrector_' + ch),
'q' + ch: ('test field', 'corrector_' + ch, 'p' + ch),
'Pi_' + ch: ('parameter field', 'corrector_' + ch, '(set-to-None)'),
'corr1_' + ch: ('parameter field', 'corrector_' + ch, '(set-to-None)'),
'corr2_' + ch: ('parameter field', 'corrector_' + ch, '(set-to-None)'),
'w' + ch: ('unknown field', 'vel_' + ch),
# lagrange mutltipliers - integral mean value
'ls' + ch: ('unknown field', 'corrector_' + ch),
'lv' + ch: ('test field', 'corrector_' + ch, 'ls' + ch),
})

options = {
'coefs': 'coefs',
'requirements': 'requirements',
'ls': 'ls', # linear solver to use
'volumes': {
'total': {
'variables': ['vol_all'],
'expression': """ev_volume.iV.Y(vol_all)""",
},
'one': {
'value': 1.0,
}
},
'output_dir': './output',
'file_per_var': True,
'coefs_filename': 'coefs_perf_' + pb_def['name'],
'coefs_info': {'eps0': eps0},
'recovery_hook': 'recovery_perf',
'multiprocessing': False,
}

for ipm in ['p', 'm']:


options['volumes'].update({
'bYM' + ipm: {
'variables': ['pM'],
'expression': "ev_volume.iS.bYM%s(pM)" % ipm,
},
'bY' + ipm: {
'variables': ['vol_all'],
'expression': "ev_volume.iS.bY%s(vol_all)" % ipm,
}
})

for ch in six.iterkeys(reg_io):
for ireg in reg_io[ch]:
options['volumes'].update({
(continues on next page)

1.5. Examples 285


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


ireg: {
'variables': ['p' + ch],
'expression': "ev_volume.iS.%s(p%s)" % (ireg, ch),
}
})

coefs = {
'vol_bYMpm': {
'regions': ['bYMp', 'bYMm'],
'expression': 'ev_volume.iS.%s(pM)',
'class': cb.VolumeFractions,
},
'filenames': {},
}

requirements = {
'corrs_one_YM': {
'variable': ['pM'],
'ebcs': ['gamma_pm_YMmchs', 'gamma_pm_bYMchs'],
'epbcs': [],
'save_name': 'corrs_one_YM',
'class': cb.CorrSetBCS,
},
}

for ipm in ['p', 'm']:


requirements.update({
'corrs_gamma_' + ipm: {
'requires': [],
'ebcs': ['gamma_pm_bYMchs'],
'epbcs': all_periodicYM,
'equations': {
'eq_gamma_pm': """dw_diffusion.iV.YM(mat2M.k, qM, pM) =
%e * dw_integrate.iS.bYM%s(qM)"""
% (1.0/param_h, ipm),
},
'class': cb.CorrOne,
'save_name': 'corrs_%s_gamma_%s' % (pb_def['name'], ipm),
},
})

for ipm2 in ['p', 'm']:


coefs.update({
'H' + ipm + ipm2: { # test+
'requires': ['corrs_gamma_' + ipm],
'set_variables': [('corr_M', 'corrs_gamma_' + ipm, 'pM')],
'expression': 'ev_integrate.iS.bYM%s(corr_M)' % ipm2,
'set_volume': 'bYp',
'class': cb.CoefOne,
},
})

(continues on next page)

286 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

def get_channel(keys, bn):


for ii in keys:
if bn in ii:
return ii[(ii.rfind(bn) + len(bn)):]

return None

def set_corrpis(variables, ir, ic, mode, **kwargs):


ch = get_channel(list(kwargs.keys()), 'pis_')
pis = kwargs['pis_' + ch]
corrs_pi = kwargs['corrs_pi' + ch]

if mode == 'row':
val = pis.states[ir]['p' + ch] + corrs_pi.states[ir]['p' + ch]
variables['corr1_' + ch].set_data(val)
elif mode == 'col':
val = pis.states[ic]['p' + ch] + corrs_pi.states[ic]['p' + ch]
variables['corr2_' + ch].set_data(val)

def set_corr_S(variables, ir, *args, **kwargs):


ch = get_channel(list(kwargs.keys()), 'pis_')
io = get_channel(list(kwargs.keys()), 'corrs_gamma_')

pis = kwargs['pis_' + ch]


corrs_gamma = kwargs['corrs_gamma_' + io]

pi = pis.states[ir]['p' + ch]
val = corrs_gamma.state['p' + ch]
variables['corr1_' + ch].set_data(pi)
variables['corr2_' + ch].set_data(val)

def set_corr_cc(variables, ir, *args, **kwargs):


ch = get_channel(list(kwargs.keys()), 'pis_')
pis = kwargs['pis_' + ch]
corrs_pi = kwargs['corrs_pi' + ch]

pi = pis.states[ir]['p' + ch]
pi = pi - nm.mean(pi)
val = pi + corrs_pi.states[ir]['p' + ch]
variables['corr1_' + ch].set_data(val)

for ch, val in six.iteritems(pb_def['channels']):


coefs.update({
'G' + ch: { # test+
'requires': ['corrs_one' + ch, 'corrs_eta' + ch],
'set_variables': [('corr1_M', 'corrs_one' + ch, 'pM'),
('corr2_M', 'corrs_eta' + ch, 'pM')],
(continues on next page)

1.5. Examples 287


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'expression': 'dw_diffusion.iV.YM(mat2M.k, corr1_M, corr2_M)',
'class': cb.CoefOne,
},
'K' + ch: { # test+
'requires': ['pis_' + ch, 'corrs_pi' + ch],
'set_variables': set_corrpis,
'expression': 'dw_diffusion.iV.Y%s(mat2%s.k, corr1_%s, corr2_%s)'\
% ((ch,) * 4),
'dim': pb_def['dim'] - 1,
'class': cb.CoefDimDim,
},
})

requirements.update({
'pis_' + ch: {
'variables': ['p' + ch],
'class': cb.ShapeDim,
},
'corrs_one' + ch: {
'variable': ['pM'],
'ebcs': ebcs_eta[ch],
'epbcs': [],
'save_name': 'corrs_%s_one%s' % (pb_def['name'], ch),
'class': cb.CorrSetBCS,
},
'corrs_eta' + ch: {
'ebcs': ebcs_eta[ch],
'epbcs': all_periodicYM,
'equations': {
'eq_eta': 'dw_diffusion.iV.YM(mat2M.k, qM, pM) = 0',
},
'class': cb.CorrOne,
'save_name': 'corrs_%s_eta%s' % (pb_def['name'], ch),
},
'corrs_pi' + ch: {
'requires': ['pis_' + ch],
'set_variables': [('Pi_' + ch, 'pis_' + ch, 'p' + ch)],
'ebcs': [],
'epbcs': all_periodicY[ch],
'lcbcs': ['imv' + ch],
'equations': {
'eq_pi': """dw_diffusion.iV.Y%s(mat2%s.k, q%s, p%s)
+ dw_dot.iV.Y%s(q%s, ls%s)
= - dw_diffusion.iV.Y%s(mat2%s.k, q%s, Pi_%s)"""
% ((ch,) * 11),
'eq_imv': 'dw_dot.iV.Y%s(lv%s, p%s) = 0' % ((ch,) * 3),
},
'dim': pb_def['dim'] - 1,
'class': cb.CorrDim,
'save_name': 'corrs_%s_pi%s' % (pb_def['name'], ch),
},
})
(continues on next page)

288 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

for ipm in ['p', 'm']:


coefs.update({
'E' + ipm + ch: { # test+
'requires': ['corrs_eta' + ch],
'set_variables': [('corr_M', 'corrs_eta' + ch, 'pM')],
'expression': 'ev_integrate.iS.bYM%s(corr_M)' % ipm,
'set_volume': 'bYp',
'class': cb.CoefOne,
},
'F' + ipm + ch: { # test+
'requires': ['corrs_one' + ch, 'corrs_gamma_' + ipm],
'set_variables': [('corr1_M', 'corrs_one' + ch, 'pM'),
('corr2_M', 'corrs_gamma_' + ipm, 'pM')],
'expression': """dw_diffusion.iV.YM(mat2M.k, corr1_M, corr2_M)
- %e * ev_integrate.iS.bYM%s(corr1_M)"""\
% (1.0/param_h, ipm),
'class': cb.CoefOne,
},
})

for i_io in range(len(val['io_nd_grp'])):


io = '%s_%d' % (ch, i_io + 1)

coefs.update({
'S' + io: { # [Rohan1] (4.28), test+
'requires': ['corrs_gamma_' + io, 'pis_' + ch],
'set_variables': set_corr_S,
'expression': 'dw_diffusion.iV.Y%s(mat2%s.k,corr1_%s,corr2_%s)'
% ((ch,) * 4),
'dim': pb_def['dim'] - 1,
'class': cb.CoefDim,
},
'P' + io: { # test+
'requires': ['pis_' + ch, 'corrs_pi' + ch],
'set_variables': set_corr_cc,
'expression': 'ev_integrate.iS.bY%s(corr1_%s)'\
% (io, ch),
'set_volume': 'bYp',
'dim': pb_def['dim'] - 1,
'class': cb.CoefDim,
},
'S_test' + io: {
'requires': ['corrs_pi' + ch],
'set_variables': [('corr1_' + ch, 'corrs_pi' + ch, 'p' + ch)],
'expression': '%e * ev_integrate.iS.bY%s(corr1_%s)'\
% (1.0 / param_h, io, ch),
'dim': pb_def['dim'] - 1,
'class': cb.CoefDim,
},
})

(continues on next page)

1.5. Examples 289


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


requirements.update({
'corrs_gamma_' + io: {
'requires': [],
'variables': ['p' + ch, 'q' + ch],
'ebcs': [],
'epbcs': all_periodicY[ch],
'lcbcs': ['imv' + ch],
'equations': {
'eq_gamma': """dw_diffusion.iV.Y%s(mat2%s.k, q%s, p%s)
+ dw_dot.iV.Y%s(q%s, ls%s)
= %e * dw_integrate.iS.bY%s(q%s)"""
% ((ch,) * 7 + (1.0/param_h, io, ch)),
'eq_imv': 'dw_dot.iV.Y%s(lv%s, p%s) = 0'
% ((ch,) * 3),
},
'class': cb.CorrOne,
'save_name': 'corrs_%s_gamma_%s' % (pb_def['name'], io),
},
})

for i_io2 in range(len(val['io_nd_grp'])):


io2 = '%s_%d' % (ch, i_io2 + 1)
io12 = '%s_%d' % (io, i_io2 + 1)
coefs.update({
'R' + io12: { # test+
'requires': ['corrs_gamma_' + io2],
'set_variables': [('corr1_' + ch, 'corrs_gamma_' + io2,
'p' + ch)],
'expression': 'ev_integrate.iS.bY%s(corr1_%s)'\
% (io, ch),
'set_volume': 'bYp',
'class': cb.CoefOne,
},
})

solvers = {
'ls': ('ls.scipy_direct', {}),
'newton': ('nls.newton', {
'i_max': 1,
})
}

290 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

homogenization/rs_correctors.py

Description
Compute homogenized elastic coefficients for a given microstructure.
source code

#!/usr/bin/env python
"""
Compute homogenized elastic coefficients for a given microstructure.
"""
from __future__ import print_function
from __future__ import absolute_import
from argparse import ArgumentParser
import sys
import six
sys.path.append('.')

import numpy as nm

from sfepy import data_dir


import sfepy.discrete.fem.periodic as per
from sfepy.homogenization.utils import define_box_regions

def define_regions(filename):
"""
Define various subdomains for a given mesh file.
"""
regions = {}
dim = 2

regions['Y'] = 'all'

eog = 'cells of group %d'


if filename.find('osteonT1') >= 0:
mat_ids = [11, 39, 6, 8, 27, 28, 9, 2, 4, 14, 12, 17, 45, 28, 15]
regions['Ym'] = ' +c '.join((eog % im) for im in mat_ids)
wx = 0.865
wy = 0.499

regions['Yc'] = 'r.Y -c r.Ym'

# Sides and corners.


regions.update(define_box_regions(2, (wx, wy)))

return dim, regions

def get_pars(ts, coor, mode=None, term=None, **kwargs):


"""
Define material parameters: :math:`D_ijkl` (elasticity), in a given region.
"""
if mode == 'qp':
dim = coor.shape[1]
(continues on next page)

1.5. Examples 291


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


sym = (dim + 1) * dim // 2

out = {}

# in 1e+10 [Pa]
lam = 1.7
mu = 0.3
o = nm.array([1.] * dim + [0.] * (sym - dim), dtype = nm.float64)
oot = nm.outer(o, o)
out['D'] = lam * oot + mu * nm.diag(o + 1.0)

for key, val in six.iteritems(out):


out[key] = nm.tile(val, (coor.shape[0], 1, 1))

channels_cells = term.region.domain.regions['Yc'].cells
n_cell = term.region.get_n_cells()
val = out['D'].reshape((n_cell, -1, 3, 3))
val[channels_cells] *= 1e-1

return out

##
# Mesh file.
filename_mesh = data_dir + '/meshes/2d/special/osteonT1_11.mesh'

##
# Define regions (subdomains, boundaries) - $Y$, $Y_i$, ...
# depending on a mesh used.
dim, regions = define_regions(filename_mesh)

functions = {
'get_pars' : (lambda ts, coors, **kwargs:
get_pars(ts, coors, **kwargs),),
'match_x_plane' : (per.match_x_plane,),
'match_y_plane' : (per.match_y_plane,),
'match_z_plane' : (per.match_z_plane,),
'match_x_line' : (per.match_x_line,),
'match_y_line' : (per.match_y_line,),
}

##
# Define fields: 'displacement' in $Y$,
# 'pressure_m' in $Y_m$.
fields = {
'displacement' : ('real', dim, 'Y', 1),
}

##
# Define corrector variables: unknown displaements: uc, test: vc
# displacement-like variables: Pi, Pi1, Pi2
variables = {
'uc' : ('unknown field', 'displacement', 0),
(continues on next page)

292 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'vc' : ('test field', 'displacement', 'uc'),
'Pi' : ('parameter field', 'displacement', 'uc'),
'Pi1' : ('parameter field', 'displacement', None),
'Pi2' : ('parameter field', 'displacement', None),
}

##
# Periodic boundary conditions.
if dim == 3:
epbcs = {
'periodic_x' : (['Left', 'Right'], {'uc.all' : 'uc.all'},
'match_x_plane'),
'periodic_y' : (['Near', 'Far'], {'uc.all' : 'uc.all'},
'match_y_plane'),
'periodic_z' : (['Top', 'Bottom'], {'uc.all' : 'uc.all'},
'match_z_plane'),
}
else:
epbcs = {
'periodic_x' : (['Left', 'Right'], {'uc.all' : 'uc.all'},
'match_y_line'),
'periodic_y' : (['Bottom', 'Top'], {'uc.all' : 'uc.all'},
'match_x_line'),
}

##
# Dirichlet boundary conditions.
ebcs = {
'fixed_u' : ('Corners', {'uc.all' : 0.0}),
}

##
# Material defining constitutive parameters of the microproblem.
materials = {
'm' : 'get_pars',
}

##
# Numerical quadratures for volume (i3 - order 3) integral terms.
integrals = {
'i3' : 3,
}

##
# Homogenized coefficients to compute.
def set_elastic(variables, ir, ic, mode, pis, corrs_rs):
mode2var = {'row' : 'Pi1', 'col' : 'Pi2'}

val = pis.states[ir, ic]['uc'] + corrs_rs.states[ir, ic]['uc']

variables[mode2var[mode]].set_data(val)

(continues on next page)

1.5. Examples 293


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


coefs = {
'E' : {
'requires' : ['pis', 'corrs_rs'],
'expression' : 'dw_lin_elastic.i3.Y(m.D, Pi1, Pi2)',
'set_variables' : set_elastic,
},
}

all_periodic = ['periodic_%s' % ii for ii in ['x', 'y', 'z'][:dim] ]


requirements = {
'pis' : {
'variables' : ['uc'],
},
##
# Steady state correctors $\bar{\omega}^{rs}$.
'corrs_rs' : {
'requires' : ['pis'],
'save_variables' : ['uc'],
'ebcs' : ['fixed_u'],
'epbcs' : all_periodic,
'equations' : {'eq' : """dw_lin_elastic.i3.Y(m.D, vc, uc)
= - dw_lin_elastic.i3.Y(m.D, vc, Pi)"""},
'set_variables' : [('Pi', 'pis', 'uc')],
'save_name' : 'corrs_elastic',
'is_linear' : True,
},
}

##
# Solvers.
solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 1,
'eps_a' : 1e-8,
'eps_r' : 1e-2,
})
}

############################################
# Mini-application below, computing the homogenized elastic coefficients.
helps = {
'no_pauses' : 'do not make pauses',
}

def main():
import os
from sfepy.base.base import spause, output
from sfepy.base.conf import ProblemConf, get_standard_keywords
from sfepy.discrete import Problem
import sfepy.homogenization.coefs_base as cb

(continues on next page)

294 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


parser = ArgumentParser(description=__doc__)
parser.add_argument('--version', action='version', version='%(prog)s')
parser.add_argument('-n', '--no-pauses',
action="store_true", dest='no_pauses',
default=False, help=helps['no_pauses'])
options = parser.parse_args()

if options.no_pauses:
def spause(*args):
output(*args)

nm.set_printoptions(precision=3)

spause(r""">>>
First, this file will be read in place of an input
(problem description) file.
Press 'q' to quit the example, press any other key to continue...""")
required, other = get_standard_keywords()
required.remove('equations')
# Use this file as the input file.
conf = ProblemConf.from_file(__file__, required, other)
print(list(conf.to_dict().keys()))
spause(r""">>>
...the read input as a dict (keys only for brevity).
['q'/other key to quit/continue...]""")

spause(r""">>>
Now the input will be used to create a Problem instance.
['q'/other key to quit/continue...]""")
problem = Problem.from_conf(conf, init_equations=False)
# The homogenization mini-apps need the output_dir.
output_dir = ''
problem.output_dir = output_dir
print(problem)
spause(r""">>>
...the Problem instance.
['q'/other key to quit/continue...]""")

spause(r""">>>
The homogenized elastic coefficient $E_{ijkl}$ is expressed
using $\Pi$ operators, computed now. In fact, those operators are permuted
coordinates of the mesh nodes.
['q'/other key to quit/continue...]""")
req = conf.requirements['pis']
mini_app = cb.ShapeDimDim('pis', problem, req)
mini_app.setup_output(save_formats=['vtk'],
file_per_var=False)
pis = mini_app()
print(pis)
spause(r""">>>
...the $\Pi$ operators.
['q'/other key to quit/continue...]""")
(continues on next page)

1.5. Examples 295


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

spause(r""">>>
Next, $E_{ijkl}$ needs so called steady state correctors $\bar{\omega}^{rs}$,
computed now.
['q'/other key to quit/continue...]""")
req = conf.requirements['corrs_rs']

save_name = req.get('save_name', '')


name = os.path.join(output_dir, save_name)

mini_app = cb.CorrDimDim('steady rs correctors', problem, req)


mini_app.setup_output(save_formats=['vtk'],
file_per_var=False)
corrs_rs = mini_app(data={'pis': pis})
print(corrs_rs)
spause(r""">>>
...the $\bar{\omega}^{rs}$ correctors.
The results are saved in: %s.%s

Try to display them with:

python postproc.py %s.%s

['q'/other key to quit/continue...]""" % (2 * (name, problem.output_format)))

spause(r""">>>
Then the volume of the domain is needed.
['q'/other key to quit/continue...]""")
volume = problem.evaluate('ev_volume.i3.Y(uc)')
print(volume)

spause(r""">>>
...the volume.
['q'/other key to quit/continue...]""")

spause(r""">>>
Finally, $E_{ijkl}$ can be computed.
['q'/other key to quit/continue...]""")
mini_app = cb.CoefSymSym('homogenized elastic tensor',
problem, conf.coefs['E'])
c_e = mini_app(volume, data={'pis': pis, 'corrs_rs' : corrs_rs})
print(r""">>>
The homogenized elastic coefficient $E_{ijkl}$, symmetric storage
with rows, columns in 11, 22, 12 ordering:""")
print(c_e)

if __name__ == '__main__':
main()

296 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

large_deformation

large_deformation/active_fibres.py

Description
Nearly incompressible hyperelastic material model with active fibres.
Large deformation is described using the total Lagrangian formulation. Models of this kind can be used in biomechanics
to model biological tissues, e.g. muscles.
Find 𝑒 such that:
∫︁ (︁ )︁
𝑆 eff (𝑒) + 𝐾(𝐽 βˆ’ 1) 𝐽𝐢 βˆ’1 : 𝛿𝐸(𝑣) d𝑉 = 0 , βˆ€π‘£ ,
Ξ©(0)

where

𝐹 deformation gradient 𝐹𝑖𝑗 = πœ•π‘‹ πœ•π‘₯𝑖


𝑗
𝐽 det(𝐹 )
𝐢 right Cauchy-Green deformation tensor 𝐢 = 𝐹 𝑇 𝐹
πœ•π‘’
𝐸(𝑒) Green strain tensor 𝐸𝑖𝑗 = 21 ( πœ•π‘‹
πœ•π‘’π‘–
𝑗
+ πœ•π‘‹π‘—π‘– + πœ•π‘’ π‘š πœ•π‘’π‘š
πœ•π‘‹π‘– πœ•π‘‹π‘— )
𝑆 eff (𝑒) effective second Piola-Kirchhoff stress tensor

The effective stress 𝑆 eff (𝑒) incorporates also the effects of the active fibres in two preferential directions:

2
2 1 βˆ‘οΈ
𝑆 eff (𝑒) = πœ‡π½ βˆ’ 3 (𝐼 βˆ’ tr(𝐢)𝐢 βˆ’1 ) + 𝜏 π‘˜ πœ”π‘˜ .
3
π‘˜=1

The first term is the neo-Hookean term and the sum add contributions of the two fibre systems. The tensors πœ” π‘˜ = π‘‘π‘˜ π‘‘π‘˜
are defined by the fibre system direction vectors π‘‘π‘˜ (unit).
For the one-dimensional tensions 𝜏 π‘˜ holds simply (π‘˜ omitted):
{οΈ‚ }οΈ‚
πœ– βˆ’ πœ€opt 2
𝜏 = 𝐴𝑓max exp βˆ’( ) ,πœ–=𝐸 :πœ”.
𝑠

1.5. Examples 297


SfePy Documentation, Release version: 2022.1+git.f9873484

source code

# -*- coding: utf-8 -*-


r"""
Nearly incompressible hyperelastic material model with active fibres.

Large deformation is described using the total Lagrangian formulation.


Models of this kind can be used in biomechanics to model biological
tissues, e.g. muscles.

Find :math:`\ul{u}` such that:

.. math::
\intl{\Omega\suz}{} \left( \ull{S}\eff(\ul{u})
+ K(J-1)\; J \ull{C}^{-1} \right) : \delta \ull{E}(\ul{v}) \difd{V}
= 0
\;, \quad \forall \ul{v} \;,

where

.. list-table::
:widths: 20 80

(continues on next page)

298 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


* - :math:`\ull{F}`
- deformation gradient :math:`F_{ij} = \pdiff{x_i}{X_j}`
* - :math:`J`
- :math:`\det(F)`
* - :math:`\ull{C}`
- right Cauchy-Green deformation tensor :math:`C = F^T F`
* - :math:`\ull{E}(\ul{u})`
- Green strain tensor :math:`E_{ij} = \frac{1}{2}(\pdiff{u_i}{X_j} +
\pdiff{u_j}{X_i} + \pdiff{u_m}{X_i}\pdiff{u_m}{X_j})`
* - :math:`\ull{S}\eff(\ul{u})`
- effective second Piola-Kirchhoff stress tensor

The effective stress :math:`\ull{S}\eff(\ul{u})` incorporates also the


effects of the active fibres in two preferential directions:

.. math::
\ull{S}\eff(\ul{u}) = \mu J^{-\frac{2}{3}}(\ull{I}
- \frac{1}{3}\tr(\ull{C}) \ull{C}^{-1})
+ \sum_{k=1}^2 \tau^k \ull{\omega}^k
\;.

The first term is the neo-Hookean term and the sum add contributions of
the two fibre systems. The tensors :math:`\ull{\omega}^k =
\ul{d}^k\ul{d}^k` are defined by the fibre system direction vectors
:math:`\ul{d}^k` (unit).

For the one-dimensional tensions :math:`\tau^k` holds simply (:math:`^k`


omitted):

.. math::
\tau = A f_{\rm max} \exp{\left\{-(\frac{\epsilon - \varepsilon_{\rm
opt}}{s})^2\right\}} \mbox{ , } \epsilon = \ull{E} : \ull{\omega}
\;.
"""
from __future__ import print_function
from __future__ import absolute_import
import numpy as nm

from sfepy import data_dir

filename_mesh = data_dir + '/meshes/3d/cylinder.mesh'

vf_matrix = 0.5
vf_fibres1 = 0.2
vf_fibres2 = 0.3

options = {
'nls' : 'newton',
'ls' : 'ls',
'ts' : 'ts',
'save_times' : 'all',
'post_process_hook' : 'stress_strain',
(continues on next page)

1.5. Examples 299


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


}

fields = {
'displacement': (nm.float64, 3, 'Omega', 1),
}

materials = {
'solid' : ({
'K' : vf_matrix * 1e3, # bulk modulus
'mu' : vf_matrix * 20e0, # shear modulus of neoHookean term
},),
'f1' : 'get_pars_fibres1',
'f2' : 'get_pars_fibres2',
}

def get_pars_fibres(ts, coors, mode=None, which=0, vf=1.0, **kwargs):


"""
Parameters
----------
ts : TimeStepper
Time stepping info.
coors : array_like
The physical domain coordinates where the parameters shound be defined.
mode : 'qp' or 'special'
Call mode.
which : int
Fibre system id.
vf : float
Fibre system volume fraction.
"""
if mode != 'qp': return

fmax = 10.0
eps_opt = 0.01
s = 1.0

tt = ts.nt * 2.0 * nm.pi

if which == 0: # system 1
fdir = nm.array([1.0, 0.0, 0.0], dtype=nm.float64)
act = 0.5 * (1.0 + nm.sin(tt - (0.5 * nm.pi)))

elif which == 1: # system 2


fdir = nm.array([0.0, 1.0, 0.0], dtype=nm.float64)
act = 0.5 * (1.0 + nm.sin(tt + (0.5 * nm.pi)))

else:
raise ValueError('unknown fibre system! (%d)' % which)

fdir.shape = (3, 1)
fdir /= nm.linalg.norm(fdir)
(continues on next page)

300 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

print(act)

shape = (coors.shape[0], 1, 1)
out = {
'fmax' : vf * nm.tile(fmax, shape),
'eps_opt' : nm.tile(eps_opt, shape),
's' : nm.tile(s, shape),
'fdir' : nm.tile(fdir, shape),
'act' : nm.tile(act, shape),
}

return out

functions = {
'get_pars_fibres1' : (lambda ts, coors, mode=None, **kwargs:
get_pars_fibres(ts, coors, mode=mode, which=0,
vf=vf_fibres1, **kwargs),),
'get_pars_fibres2' : (lambda ts, coors, mode=None, **kwargs:
get_pars_fibres(ts, coors, mode=mode, which=1,
vf=vf_fibres2, **kwargs),),
}

variables = {
'u' : ('unknown field', 'displacement', 0),
'v' : ('test field', 'displacement', 'u'),
}

regions = {
'Omega' : 'all',
'Left' : ('vertices in (x < 0.001)', 'facet'),
'Right' : ('vertices in (x > 0.099)', 'facet'),
}

##
# Dirichlet BC.
ebcs = {
'l' : ('Left', {'u.all' : 0.0}),
}

##
# Balance of forces.
integral_1 = {
'name' : 'i',
'order' : 1,
}
equations = {
'balance'
: """dw_tl_he_neohook.i.Omega( solid.mu, v, u )
+ dw_tl_bulk_penalty.i.Omega( solid.K, v, u )
+ dw_tl_fib_a.i.Omega( f1.fmax, f1.eps_opt, f1.s, f1.fdir, f1.act,
v, u )
(continues on next page)

1.5. Examples 301


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


+ dw_tl_fib_a.i.Omega( f2.fmax, f2.eps_opt, f2.s, f2.fdir, f2.act,
v, u )
= 0""",
}

def stress_strain(out, problem, state, extend=False):


from sfepy.base.base import Struct, debug

ev = problem.evaluate
strain = ev('dw_tl_he_neohook.i.Omega( solid.mu, v, u )',
mode='el_avg', term_mode='strain')
out['green_strain'] = Struct(name='output_data',
mode='cell', data=strain, dofs=None)

stress = ev('dw_tl_he_neohook.i.Omega( solid.mu, v, u )',


mode='el_avg', term_mode='stress')
out['neohook_stress'] = Struct(name='output_data',
mode='cell', data=stress, dofs=None )

stress = ev('dw_tl_bulk_penalty.i.Omega( solid.K, v, u )',


mode='el_avg', term_mode= 'stress')
out['bulk_stress'] = Struct(name='output_data',
mode='cell', data=stress, dofs=None)

return out

##
# Solvers etc.
solver_0 = {
'name' : 'ls',
'kind' : 'ls.scipy_direct',
}

solver_1 = {
'name' : 'newton',
'kind' : 'nls.newton',

'i_max' : 7,
'eps_a' : 1e-10,
'eps_r' : 1.0,
'macheps' : 1e-16,
'lin_red' : 1e-2, # Linear system error < (eps_a * lin_red).
'ls_red' : 0.1,
'ls_red_warp': 0.001,
'ls_on' : 1.1,
'ls_min' : 1e-5,
'check' : 0,
'delta' : 1e-6,
}

solver_2 = {
'name' : 'ts',
(continues on next page)

302 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'kind' : 'ts.simple',

't0' : 0,
't1' : 1,
'dt' : None,
'n_step' : 21, # has precedence over dt!
'verbose' : 1,
}

large_deformation/balloon.py

Description
Inflation of a Mooney-Rivlin hyperelastic balloon.
This example serves as a verification of the membrane term (dw_tl_membrane, TLMembraneTerm) implementation.
Following Rivlin 1952 and Dumais, the analytical relation between a relative stretch 𝐿 = π‘Ÿ/π‘Ÿ0 of a thin (membrane)
sphere made of the Mooney-Rivlin material of the undeformed radius π‘Ÿ0 , membrane thickness β„Ž0 and the inner pressure
𝑝 is
β„Ž0 1 1
𝑝=4 ( βˆ’ 7 )(𝑐1 + 𝑐2 𝐿2 ) ,
π‘Ÿ0 𝐿 𝐿
where 𝑐1 , 𝑐2 are the Mooney-Rivlin material parameters.
In the equations below, only the surface of the domain is mechanically important - a stiff 2D membrane is embedded
in the 3D space and coincides with the balloon surface. The volume is very soft, to simulate a fluid-filled cavity. A
similar model could be used to model e.g. plant cells. The balloon surface is loaded by prescribing the inner volume
change πœ”(𝑑). The fluid pressure in the cavity is a single scalar value, enforced by the 'integral_mean_value' linear
combination condition.
Find 𝑒(𝑋) and a constant 𝑝 such that:
β€’ balance of forces:
∫︁ (︁ )︁ ∫︁
𝑆 eff (𝑒) βˆ’ 𝑝 𝐽𝐢 βˆ’1 : 𝛿𝐸(𝑣; 𝑣) d𝑉 + 𝑆 eff (˜
𝑒)𝛿𝐸(˜
𝑒; π‘£Λœ)β„Ž0 d𝑆 = 0 , βˆ€π‘£ ∈ [𝐻01 (Ω)]3 ,
Ξ©(0) Ξ“(0)

β€’ volume conservation:
∫︁
[πœ”(𝑑) βˆ’ 𝐽(𝑒)] π‘ž 𝑑π‘₯ = 0 βˆ€π‘ž ∈ 𝐿2 (Ω) ,
Ξ©0

where

𝐹 deformation gradient 𝐹𝑖𝑗 = πœ•π‘‹ πœ•π‘₯𝑖


𝑗
𝐽 det(𝐹 )
𝐢 right Cauchy-Green deformation tensor 𝐢 = 𝐹 𝑇 𝐹
πœ•π‘’
𝐸(𝑒) Green strain tensor 𝐸𝑖𝑗 = 21 ( πœ•π‘‹
πœ•π‘’π‘–
𝑗
+ πœ•π‘‹π‘—π‘– + πœ•π‘’ π‘š πœ•π‘’π‘š
πœ•π‘‹π‘– πœ•π‘‹π‘— )
𝑆 eff (𝑒) effective second Piola-Kirchhoff stress tensor

1.5. Examples 303


SfePy Documentation, Release version: 2022.1+git.f9873484

The effective stress 𝑆 eff (𝑒) is given by:

2 1 4 2
𝑆 eff (𝑒) = πœ‡π½ βˆ’ 3 (𝐼 βˆ’ tr(𝐢)𝐢 βˆ’1 ) + πœ…π½ βˆ’ 3 (tr(𝐢𝐼 βˆ’ 𝐢 βˆ’ ((tr 𝐢)2 βˆ’ tr (𝐢 2 ))𝐢 βˆ’1 ) .
3 6
The 𝑒
˜ and π‘£Λœ variables correspond to 𝑒, 𝑣, respectively, transformed to the membrane coordinate frame.
Use the following command to show a comparison of the FEM solution with the above analytical relation (notice the
nonlinearity of the dependence):

python simple.py examples/large_deformation/balloon.py -d 'plot: True'

The agreement should be very good, even though the mesh is coarse.
View the results using:

python postproc.py unit_ball.h5 --wireframe -b -d'u,plot_displacements,rel_scaling=1' --


Λ“β†’step=-1

This example uses the adaptive time-stepping solver ('ts.adaptive') with the default adaptivity function
adapt_time_step(). Plot the used time steps by:

python script/plot_times.py unit_ball.h5

source code

304 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

r"""
Inflation of a Mooney-Rivlin hyperelastic balloon.

This example serves as a verification of the membrane term (``dw_tl_membrane``,


:class:`TLMembraneTerm <sfepy.terms.terms_membrane.TLMembraneTerm>`)
implementation.

Following Rivlin 1952 and Dumais, the analytical relation between a


relative stretch :math:`L = r / r_0` of a thin (membrane) sphere made of the
Mooney-Rivlin material of the undeformed radius :math:`r_0`, membrane
thickness :math:`h_0` and the inner pressure :math:`p` is

.. math::

p = 4 \frac{h_0}{r_0} (\frac{1}{L} - \frac{1}{L^7}) (c_1 + c_2 L^2) \;,

where :math:`c_1`, :math:`c_2` are the Mooney-Rivlin material parameters.

In the equations below, only the surface of the domain is mechanically


important - a stiff 2D membrane is embedded in the 3D space and coincides with
the balloon surface. The volume is very soft, to simulate a fluid-filled
cavity. A similar model could be used to model e.g. plant cells. The balloon
surface is loaded by prescribing the inner volume change :math:`\omega(t)`.
The fluid pressure in the cavity is a single scalar value, enforced by the
``'integral_mean_value'`` linear combination condition.

Find :math:`\ul{u}(\ul{X})` and a constant :math:`p` such that:

- balance of forces:

.. math::
\intl{\Omega\suz}{} \left( \ull{S}\eff(\ul{u})
- p\; J \ull{C}^{-1} \right) : \delta \ull{E}(\ul{v}; \ul{v}) \difd{V}
+ \intl{\Gamma\suz}{} \ull{S}\eff(\tilde{\ul{u}}) \delta
\ull{E}(\tilde{\ul{u}}; \tilde{\ul{v}}) h_0 \difd{S}
= 0 \;, \quad \forall \ul{v} \in [H^1_0(\Omega)]^3 \;,

- volume conservation:

.. math::
\int\limits_{\Omega_0} \left[\omega(t)-J(u)\right] q\, dx = 0
\qquad \forall q \in L^2(\Omega) \;,

where

.. list-table::
:widths: 20 80

* - :math:`\ull{F}`
- deformation gradient :math:`F_{ij} = \pdiff{x_i}{X_j}`
* - :math:`J`
- :math:`\det(F)`
* - :math:`\ull{C}`
(continues on next page)

1.5. Examples 305


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


- right Cauchy-Green deformation tensor :math:`C = F^T F`
* - :math:`\ull{E}(\ul{u})`
- Green strain tensor :math:`E_{ij} = \frac{1}{2}(\pdiff{u_i}{X_j} +
\pdiff{u_j}{X_i} + \pdiff{u_m}{X_i}\pdiff{u_m}{X_j})`
* - :math:`\ull{S}\eff(\ul{u})`
- effective second Piola-Kirchhoff stress tensor

The effective stress :math:`\ull{S}\eff(\ul{u})` is given by:

.. math::
\ull{S}\eff(\ul{u}) = \mu J^{-\frac{2}{3}}(\ull{I}
- \frac{1}{3}\tr(\ull{C}) \ull{C}^{-1})
+ \kappa J^{-\frac{4}{3}} (\tr(\ull{C}\ull{I} - \ull{C}
- \frac{2}{6}((\tr{\ull{C}})^2 - \tr{(\ull{C}^2)})\ull{C}^{-1})
\;.

The :math:`\tilde{\ul{u}}` and :math:`\tilde{\ul{v}}` variables correspond to


:math:`\ul{u}`, :math:`\ul{v}`, respectively, transformed to the membrane
coordinate frame.

Use the following command to show a comparison of the FEM solution with the
above analytical relation (notice the nonlinearity of the dependence)::

python simple.py sfepy/examples/large_deformation/balloon.py -d 'plot: True'

The agreement should be very good, even though the mesh is coarse.

View the results using::

python postproc.py unit_ball.h5 --wireframe -b -d'u,plot_displacements,rel_scaling=1' --


Λ“β†’step=-1

This example uses the adaptive time-stepping solver (``'ts.adaptive'``) with


the default adaptivity function :func:`adapt_time_step()
<sfepy.solvers.ts_solvers.adapt_time_step>`. Plot the used time steps by::

python script/plot_times.py unit_ball.h5


"""
import os
import numpy as nm

from sfepy.base.base import Output


from sfepy.discrete.fem import MeshIO
from sfepy.linalg import get_coors_in_ball
from sfepy import data_dir

output = Output('balloon:')

def get_nodes(coors, radius, eps, mode):


if mode == 'ax1':
centre = nm.array([0.0, 0.0, -radius], dtype=nm.float64)

(continues on next page)

306 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


elif mode == 'ax2':
centre = nm.array([0.0, 0.0, radius], dtype=nm.float64)

elif mode == 'equator':


centre = nm.array([radius, 0.0, 0.0], dtype=nm.float64)

else:
raise ValueError('unknown mode %s!' % mode)

return get_coors_in_ball(coors, centre, eps)

def get_volume(ts, coors, region=None):


rs = 1.0 + 1.0 * ts.time

rv = get_rel_volume(rs)
output('relative stretch:', rs)
output('relative volume:', rv)

out = nm.empty((coors.shape[0],), dtype=nm.float64)


out.fill(rv)

return out

def get_rel_volume(rel_stretch):
"""
Get relative volume V/V0 from relative stretch r/r0 of a ball.
"""
return nm.power(rel_stretch, 3.0)

def get_rel_stretch(rel_volume):
"""
Get relative stretch r/r0 from relative volume V/V0 of a ball.
"""
return nm.power(rel_volume, 1.0/3.0)

def get_balloon_pressure(rel_stretch, h0, r0, c1, c2):


"""
Rivlin 1952 + Dumais:

P = 4*h0/r0 * (1/L-1/L^7).*(C1+L^2*C2)
"""
L = rel_stretch
p = 4.0 * h0 / r0 * (1.0/L - 1.0/L**7) * (c1 + c2 * L**2)

return p

def plot_radius(problem, state):


import matplotlib.pyplot as plt

from sfepy.postprocess.time_history import extract_time_history

ths, ts = extract_time_history('unit_ball.h5', 'p e 0')


(continues on next page)

1.5. Examples 307


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

p = ths['p'][0]
L = 1.0 + ts.times[:p.shape[0]]

L2 = 1.0 + nm.linspace(ts.times[0], ts.times[-1], 1000)


p2 = get_balloon_pressure(L2, 1e-2, 1, 3e5, 3e4)

plt.rcParams['lines.linewidth'] = 3
plt.rcParams['font.size'] = 16

plt.plot(L2, p2, 'r', label='theory')


plt.plot(L, p, 'b*', ms=12, label='FEM')

plt.title('Mooney-Rivlin hyperelastic balloon inflation')


plt.xlabel(r'relative stretch $r/r_0$')
plt.ylabel(r'pressure $p$')

plt.legend(loc='best')

fig = plt.gcf()
fig.savefig('balloon_pressure_stretch.pdf')

plt.show()

def define(plot=False):
filename_mesh = data_dir + '/meshes/3d/unit_ball.mesh'

conf_dir = os.path.dirname(__file__)
io = MeshIO.any_from_filename(filename_mesh, prefix_dir=conf_dir)
bbox = io.read_bounding_box()
dd = bbox[1] - bbox[0]

radius = bbox[1, 0]
eps = 1e-8 * dd[0]

options = {
'nls' : 'newton',
'ls' : 'ls',
'ts' : 'ts',
'save_times' : 'all',
'output_dir' : '.',
'output_format' : 'h5',
}

if plot:
options['post_process_hook_final'] = plot_radius

fields = {
'displacement': (nm.float64, 3, 'Omega', 1),
'pressure': (nm.float64, 1, 'Omega', 0),
}

(continues on next page)

308 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


materials = {
'solid' : ({
'mu' : 50, # shear modulus of neoHookean term
'kappa' : 0.0, # shear modulus of Mooney-Rivlin term
},),
'walls' : ({
'mu' : 3e5, # shear modulus of neoHookean term
'kappa' : 3e4, # shear modulus of Mooney-Rivlin term
'h0' : 1e-2, # initial thickness of wall membrane
},),
}

variables = {
'u' : ('unknown field', 'displacement', 0),
'v' : ('test field', 'displacement', 'u'),
'p' : ('unknown field', 'pressure', 1),
'q' : ('test field', 'pressure', 'p'),
'omega' : ('parameter field', 'pressure', {'setter' : 'get_volume'}),
}

regions = {
'Omega' : 'all',
'Ax1' : ('vertices by get_ax1', 'vertex'),
'Ax2' : ('vertices by get_ax2', 'vertex'),
'Equator' : ('vertices by get_equator', 'vertex'),
'Surface' : ('vertices of surface', 'facet'),
}

ebcs = {
'fix1' : ('Ax1', {'u.all' : 0.0}),
'fix2' : ('Ax2', {'u.[0, 1]' : 0.0}),
'fix3' : ('Equator', {'u.1' : 0.0}),
}

lcbcs = {
'pressure' : ('Omega', {'p.all' : None}, None, 'integral_mean_value'),
}

equations = {
'balance'
: """dw_tl_he_neohook.2.Omega(solid.mu, v, u)
+ dw_tl_he_mooney_rivlin.2.Omega(solid.kappa, v, u)
+ dw_tl_membrane.2.Surface(walls.mu, walls.kappa, walls.h0, v, u)
+ dw_tl_bulk_pressure.2.Omega(v, u, p)
= 0""",
'volume'
: """dw_tl_volume.2.Omega(q, u)
= dw_dot.2.Omega(q, omega)""",
}

solvers = {
'ls' : ('ls.scipy_direct', {}),
(continues on next page)

1.5. Examples 309


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'newton' : ('nls.newton', {
'i_max' : 6,
'eps_a' : 1e-4,
'eps_r' : 1e-8,
'macheps' : 1e-16,
'lin_red' : 1e-2,
'ls_red' : 0.5,
'ls_red_warp': 0.1,
'ls_on' : 100.0,
'ls_min' : 1e-5,
'check' : 0,
'delta' : 1e-6,
'is_plot' : False,
'problem' : 'nonlinear',
}),
'ts' : ('ts.adaptive', {
't0' : 0.0,
't1' : 5.0,
'dt' : None,
'n_step' : 11,

'dt_red_factor' : 0.8,
'dt_red_max' : 1e-3,
'dt_inc_factor' : 1.25,
'dt_inc_on_iter' : 4,
'dt_inc_wait' : 3,

'verbose' : 1,
'quasistatic' : True,
}),
}

functions = {
'get_ax1' : (lambda coors, domain:
get_nodes(coors, radius, eps, 'ax1'),),
'get_ax2' : (lambda coors, domain:
get_nodes(coors, radius, eps, 'ax2'),),
'get_equator' : (lambda coors, domain:
get_nodes(coors, radius, eps, 'equator'),),
'get_volume' : (get_volume,),
}

return locals()

310 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

large_deformation/compare_elastic_materials.py

Description
Compare various elastic materials w.r.t. uniaxial tension/compression test.
Requires Matplotlib.
source code

#!/usr/bin/env python
"""
Compare various elastic materials w.r.t. uniaxial tension/compression test.

Requires Matplotlib.
"""
from __future__ import absolute_import
from argparse import ArgumentParser, RawDescriptionHelpFormatter
import sys
import six
sys.path.append('.')

import numpy as nm

def define():
"""Define the problem to solve."""
from sfepy.discrete.fem.meshio import UserMeshIO
from sfepy.mesh.mesh_generators import gen_block_mesh
from sfepy.mechanics.matcoefs import stiffness_from_lame

def mesh_hook(mesh, mode):


"""
Generate the block mesh.
"""
if mode == 'read':
mesh = gen_block_mesh([2, 2, 3], [2, 2, 4], [0, 0, 1.5], name='el3',
verbose=False)
return mesh

elif mode == 'write':


pass

filename_mesh = UserMeshIO(mesh_hook)

options = {
'nls' : 'newton',
'ls' : 'ls',
'ts' : 'ts',
'save_times' : 'all',
}

functions = {
'linear_tension' : (linear_tension,),
'linear_compression' : (linear_compression,),
(continues on next page)

1.5. Examples 311


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


'empty' : (lambda ts, coor, mode, region, ig: None,),
}

fields = {
'displacement' : ('real', 3, 'Omega', 1),
}

# Coefficients are chosen so that the tangent stiffness is the same for all
# material for zero strains.
# Young modulus = 10 kPa, Poisson's ratio = 0.3
materials = {
'solid' : ({
'K' : 8.333, # bulk modulus
'mu_nh' : 3.846, # shear modulus of neoHookean term
'mu_mr' : 1.923, # shear modulus of Mooney-Rivlin term
'kappa' : 1.923, # second modulus of Mooney-Rivlin term
# elasticity for LE term
'D' : stiffness_from_lame(dim=3, lam=5.769, mu=3.846),
},),
'load' : 'empty',
}

variables = {
'u' : ('unknown field', 'displacement', 0),
'v' : ('test field', 'displacement', 'u'),
}

regions = {
'Omega' : 'all',
'Bottom' : ('vertices in (z < 0.1)', 'facet'),
'Top' : ('vertices in (z > 2.9)', 'facet'),
}

ebcs = {
'fixb' : ('Bottom', {'u.all' : 0.0}),
'fixt' : ('Top', {'u.[0,1]' : 0.0}),
}

integrals = {
'i' : 1,
'isurf' : 2,
}
equations = {
'linear' : """dw_lin_elastic.i.Omega(solid.D, v, u)
= dw_surface_ltr.isurf.Top(load.val, v)""",
'neo-Hookean' : """dw_tl_he_neohook.i.Omega(solid.mu_nh, v, u)
+ dw_tl_bulk_penalty.i.Omega(solid.K, v, u)
= dw_surface_ltr.isurf.Top(load.val, v)""",
'Mooney-Rivlin' : """dw_tl_he_neohook.i.Omega(solid.mu_mr, v, u)
+ dw_tl_he_mooney_rivlin.i.Omega(solid.kappa, v, u)
+ dw_tl_bulk_penalty.i.Omega(solid.K, v, u)
= dw_surface_ltr.isurf.Top(load.val, v)""",
(continues on next page)

312 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


}

solvers = {
'ls' : ('ls.scipy_direct', {}),
'newton' : ('nls.newton', {
'i_max' : 5,
'eps_a' : 1e-10,
'eps_r' : 1.0,
}),
'ts' : ('ts.simple', {
't0' : 0,
't1' : 1,
'dt' : None,
'n_step' : 101, # has precedence over dt!
'verbose' : 1,
}),
}

return locals()

##
# Pressure tractions.
def linear_tension(ts, coor, mode=None, **kwargs):
if mode == 'qp':
val = nm.tile(0.1 * ts.step, (coor.shape[0], 1, 1))
return {'val' : val}

def linear_compression(ts, coor, mode=None, **kwargs):


if mode == 'qp':
val = nm.tile(-0.1 * ts.step, (coor.shape[0], 1, 1))
return {'val' : val}

def store_top_u(displacements):
"""Function _store() will be called at the end of each loading step. Top
displacements will be stored into `displacements`."""
def _store(problem, ts, state):

top = problem.domain.regions['Top']
top_u = problem.get_variables()['u'].get_state_in_region(top)
displacements.append(nm.mean(top_u[:,-1]))

return _store

def solve_branch(problem, branch_function):


displacements = {}
for key, eq in six.iteritems(problem.conf.equations):
problem.set_equations({key : eq})

load = problem.get_materials()['load']
load.set_function(branch_function)

(continues on next page)

1.5. Examples 313


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


out = []
problem.solve(save_results=False, step_hook=store_top_u(out))
displacements[key] = nm.array(out, dtype=nm.float64)

return displacements

helps = {
'no_plot' : 'do not show plot window',
}

def main():
from sfepy.base.base import output
from sfepy.base.conf import ProblemConf, get_standard_keywords
from sfepy.discrete import Problem
from sfepy.base.plotutils import plt

parser = ArgumentParser(description=__doc__,
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('--version', action='version', version='%(prog)s')
parser.add_argument('-n', '--no-plot',
action="store_true", dest='no_plot',
default=False, help=helps['no_plot'])
options = parser.parse_args()

required, other = get_standard_keywords()


# Use this file as the input file.
conf = ProblemConf.from_file(__file__, required, other)

# Create problem instance, but do not set equations.


problem = Problem.from_conf(conf, init_equations=False)

# Solve the problem. Output is ignored, results stored by using the


# step_hook.
u_t = solve_branch(problem, linear_tension)
u_c = solve_branch(problem, linear_compression)

# Get pressure load by calling linear_*() for each time step.


ts = problem.get_timestepper()
load_t = nm.array([linear_tension(ts, nm.array([[0.0]]), 'qp')['val']
for aux in ts.iter_from(0)],
dtype=nm.float64).squeeze()
load_c = nm.array([linear_compression(ts, nm.array([[0.0]]), 'qp')['val']
for aux in ts.iter_from(0)],
dtype=nm.float64).squeeze()

# Join the branches.


displacements = {}
for key in u_t.keys():
displacements[key] = nm.r_[u_c[key][::-1], u_t[key]]
load = nm.r_[load_c[::-1], load_t]

(continues on next page)

314 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


if plt is None:
output('matplotlib cannot be imported, printing raw data!')
output(displacements)
output(load)
else:
legend = []
for key, val in six.iteritems(displacements):
plt.plot(load, val)
legend.append(key)

plt.legend(legend, loc = 2)
plt.xlabel('tension [kPa]')
plt.ylabel('displacement [mm]')
plt.grid(True)

plt.gcf().savefig('pressure_displacement.png')

if not options.no_plot:
plt.show()

if __name__ == '__main__':
main()

large_deformation/gen_yeoh_tl_up_interactive.py

Description
This example shows the use of the dw_tl_he_genyeoh hyperelastic term, whose contribution to the deformation energy
density per unit reference volume is given by
(οΈ€ )︀𝑝
π‘Š = 𝐾 𝐼1 βˆ’ 3

where 𝐼 1 is the first main invariant of the deviatoric part of the right Cauchy-Green deformation tensor 𝐢 and K and p
are its parameters.
This term may be used to implement the generalized Yeoh hyperelastic material model [1] by adding three such terms:
(οΈ€ )οΈ€π‘š (οΈ€ )︀𝑝 (οΈ€ )οΈ€π‘ž
π‘Š = 𝐾1 𝐼 1 βˆ’ 3 + 𝐾2 𝐼 1 βˆ’ 3 + 𝐾3 𝐼 1 βˆ’ 3

where the coefficients 𝐾1 , 𝐾2 , 𝐾3 and exponents π‘š, 𝑝, π‘ž are material parameters. Only a single term is used in this
example for the sake of simplicity.
Components of the second Piola-Kirchhoff stress are in the case of an incompressible material

πœ•π‘Š βˆ’1 βˆ’π‘‡
𝑆𝑖𝑗 = 2 βˆ’ 𝑝 πΉπ‘–π‘˜ πΉπ‘˜π‘— ,
πœ•πΆπ‘–π‘—

where 𝑝 is the hydrostatic pressure.


The large deformation is described using the total Lagrangian formulation in this example. The incompressibility is
treated by mixed displacement-pressure formulation. The weak formulation is: Find the displacement field 𝑒 and

1.5. Examples 315


SfePy Documentation, Release version: 2022.1+git.f9873484

pressure field 𝑝 such that:


∫︁
𝑆 eff (𝑒, 𝑝) : 𝐸(𝑣) d𝑉 = 0 , βˆ€π‘£ ,
Ξ©(0)
∫︁
π‘ž (𝐽(𝑒) βˆ’ 1) d𝑉 = 0 , βˆ€π‘ž .
Ξ©(0)

The following formula holds for the axial true (Cauchy) stress in the case of uniaxial stress:
(οΈ‚ )οΈ‚π‘šβˆ’1 (οΈ‚ )οΈ‚
2 2 1
𝜎(πœ†) = π‘š 𝐾1 πœ†2 + βˆ’3 πœ†βˆ’ 2 ,
3 πœ† πœ†

where πœ† = 𝑙/𝑙0 is the prescribed stretch (𝑙0 and 𝑙 being the original and deformed specimen length respectively).
The boundary conditions are set so that a state of uniaxial stress is achieved, i.e. appropriate components of displace-
ment are fixed on the β€œLeft”, β€œBottom”, and β€œNear” faces and a monotonously increasing displacement is prescribed
on the β€œRight” face. This prescribed displacement is then used to calculate πœ† and to convert the second Piola-Kirchhoff
stress to the true (Cauchy) stress.

Note on material parameters

The three-term generalized Yeoh model is meant to be used for modelling of filled rubbers. The following choice of
parameters is suggested [1] based on experimental data and stability considerations:
𝐾1 > 0,
𝐾2 < 0,
𝐾3 > 0,
0.7 < π‘š < 1,
π‘š < 𝑝 < π‘ž.

Usage Examples

Default options:

$ python examples/large_deformation/gen_yeoh_tl_up_interactive.py

To show a comparison of stress against the analytic formula:

$ python examples/large_deformation/gen_yeoh_tl_up_interactive.py -p

Using different mesh fineness:

$ python examples/large_deformation/gen_yeoh_tl_up_interactive.py \
--shape "5, 5, 5"

Different dimensions of the computational domain:

$ python examples/large_deformation/gen_yeoh_tl_up_interactive.py \
--dims "2, 1, 3"

Different length of time interval and/or number of time steps:

316 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

$ python examples/large_deformation/gen_yeoh_tl_up_interactive.py \
-t 0,15,21

Use higher approximation order (the -t option to decrease the time step is required for convergence here):

$ python examples/large_deformation/gen_yeoh_tl_up_interactive.py \
--order 2 -t 0,2,21

Change material parameters:

$ python examples/large_deformation/gen_yeoh_tl_up_interactive.py -m 2,1

View the results using resview.py

Show pressure on deformed mesh (use PgDn/PgUp to jump forward/back):

$ python resview.py --fields=p:f1:wu:p1 domain.??.vtk

Show the axial component of stress (second Piola-Kirchhoff):

$ python resview.py --fields=stress:c0 domain.??.vtk

[1] Travis W. Hohenberger, Richard J. Windslow, Nicola M. Pugno, James J. C. Busfield. Aconstitutive Model For
Both Lowand High Strain Nonlinearities In Highly Filled Elastomers And Implementation With User-Defined Material
Subroutines In Abaqus. Rubber Chemistry And Technology, Vol. 92, No. 4, Pp. 653-686 (2019)
source code

#!/usr/bin/env python
r"""
This example shows the use of the `dw_tl_he_genyeoh` hyperelastic term, whose
contribution to the deformation energy density per unit reference volume is
given by

.. math::
W = K \, \left( \overline I_1 - 3 \right)^{p}

where :math:`\overline I_1` is the first main invariant of the deviatoric part
of the right Cauchy-Green deformation tensor :math:`\ull{C}` and `K` and `p`
are its parameters.

This term may be used to implement the generalized Yeoh hyperelastic material
model [1] by adding three such terms:

.. math::
W =
K_1 \, \left( \overline I_1 - 3 \right)^{m}
+K_2 \, \left( \overline I_1 - 3 \right)^{p}
+K_3 \, \left( \overline I_1 - 3 \right)^{q}

where the coefficients :math:`K_1, K_2, K_3` and exponents :math:`m, p, q` are
material parameters. Only a single term is used in this example for the sake of
simplicity.
(continues on next page)

1.5. Examples 317


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

Components of the second Piola-Kirchhoff stress are in the case of an


incompressible material

.. math::
S_{ij} = 2 \, \pdiff{W}{C_{ij}} - p \, F^{-1}_{ik} \, F^{-T}_{kj} \;,

where :math:`p` is the hydrostatic pressure.

The large deformation is described using the total Lagrangian formulation in


this example. The incompressibility is treated by mixed displacement-pressure
formulation. The weak formulation is:
Find the displacement field :math:`\ul{u}` and pressure field :math:`p`
such that:

.. math::
\intl{\Omega\suz}{} \ull{S}\eff(\ul{u}, p) : \ull{E}(\ul{v})
\difd{V} = 0
\;, \quad \forall \ul{v} \;,

\intl{\Omega\suz}{} q\, (J(\ul{u})-1) \difd{V} = 0


\;, \quad \forall q \;.

The following formula holds for the axial true (Cauchy) stress in the case of
uniaxial stress:

.. math::
\sigma(\lambda) =
\frac{2}{3} \, m \, K_1 \,
\left( \lambda^2 + \frac{2}{\lambda} - 3 \right)^{m-1} \,
\left( \lambda - \frac{1}{\lambda^2} \right) \;,

where :math:`\lambda = l/l_0` is the prescribed stretch (:math:`l_0` and


:math:`l` being the original and deformed specimen length respectively).

The boundary conditions are set so that a state of uniaxial stress is achieved,
i.e. appropriate components of displacement are fixed on the "Left", "Bottom",
and "Near" faces and a monotonously increasing displacement is prescribed on
the "Right" face. This prescribed displacement is then used to calculate
:math:`\lambda` and to convert the second Piola-Kirchhoff stress to the true
(Cauchy) stress.

Note on material parameters


---------------------------

The three-term generalized Yeoh model is meant to be used for modelling of


filled rubbers. The following choice of parameters is suggested [1] based on
experimental data and stability considerations:

:math:`K_1 > 0`,

:math:`K_2 < 0`,


(continues on next page)

318 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

:math:`K_3 > 0`,

:math:`0.7 < m < 1`,

:math:`m < p < q`.

Usage Examples
--------------

Default options::

$ python sfepy/examples/large_deformation/gen_yeoh_tl_up_interactive.py

To show a comparison of stress against the analytic formula::

$ python sfepy/examples/large_deformation/gen_yeoh_tl_up_interactive.py -p

Using different mesh fineness::

$ python sfepy/examples/large_deformation/gen_yeoh_tl_up_interactive.py \
--shape "5, 5, 5"

Different dimensions of the computational domain::

$ python sfepy/examples/large_deformation/gen_yeoh_tl_up_interactive.py \
--dims "2, 1, 3"

Different length of time interval and/or number of time steps::

$ python sfepy/examples/large_deformation/gen_yeoh_tl_up_interactive.py \
-t 0,15,21

Use higher approximation order (the ``-t`` option to decrease the time step is
required for convergence here)::

$ python sfepy/examples/large_deformation/gen_yeoh_tl_up_interactive.py \
--order 2 -t 0,2,21

Change material parameters::

$ python sfepy/examples/large_deformation/gen_yeoh_tl_up_interactive.py -m 2,1

View the results using ``resview.py``


-------------------------------------

Show pressure on deformed mesh (use PgDn/PgUp to jump forward/back)::

$ python resview.py --fields=p:f1:wu:p1 domain.??.vtk

Show the axial component of stress (second Piola-Kirchhoff)::

(continues on next page)

1.5. Examples 319


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


$ python resview.py --fields=stress:c0 domain.??.vtk

[1] Travis W. Hohenberger, Richard J. Windslow, Nicola M. Pugno, James J. C.


Busfield. Aconstitutive Model For Both Lowand High Strain Nonlinearities In
Highly Filled Elastomers And Implementation With User-Defined Material
Subroutines In Abaqus. Rubber Chemistry And Technology, Vol. 92, No. 4, Pp.
653-686 (2019)
"""
from __future__ import print_function, absolute_import
import argparse
import sys

SFEPY_DIR = '.'
sys.path.append(SFEPY_DIR)

import matplotlib.pyplot as plt


import numpy as np

from sfepy.base.base import IndexedStruct, Struct


from sfepy.discrete import (
FieldVariable, Material, Integral, Function, Equation, Equations, Problem)
from sfepy.discrete.conditions import Conditions, EssentialBC
from sfepy.discrete.fem import FEDomain, Field
from sfepy.homogenization.utils import define_box_regions
from sfepy.mesh.mesh_generators import gen_block_mesh
from sfepy.solvers.ls import ScipyDirect
from sfepy.solvers.nls import Newton
from sfepy.solvers.ts_solvers import SimpleTimeSteppingSolver
from sfepy.terms import Term

DIMENSION = 3

def get_displacement(ts, coors, bc=None, problem=None):


"""
Define the time-dependent displacement.
"""
out = 1. * ts.time * coors[:, 0]
return out

def _get_analytic_stress(stretches, coef, exp):


out = np.array([
2 * coef * exp * (stretch**2 + 2 / stretch - 3)**(exp - 1)
* (stretch - stretch**-2)
if (stretch**2 + 2 / stretch > 3) else 0.
for stretch in stretches])
return out

def plot_graphs(
material_parameters, global_stress, global_displacement,
undeformed_length):
"""
Plot a comparison of the nominal stress computed by the FEM and using the
(continues on next page)

320 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


analytic formula.

Parameters
----------
material_parameters : list or tuple of float
The K_1 coefficient and exponent m.
global_displacement
The total displacement for each time step, from the FEM.
global_stress
The true (Cauchy) stress for each time step, from the FEM.
undeformed_length : float
The length of the undeformed specimen.
"""
coef, exp = material_parameters

stretch = 1 + np.array(global_displacement) / undeformed_length

# axial stress values


stress_fem_2pk = np.array([sig for sig in global_stress])
stress_fem = stress_fem_2pk * stretch
stress_analytic = _get_analytic_stress(stretch, coef, exp)

fig, (ax_stress, ax_difference) = plt.subplots(nrows=2, sharex=True)

ax_stress.plot(stretch, stress_fem, '.-', label='FEM')


ax_stress.plot(stretch, stress_analytic, '--', label='analytic')

ax_difference.plot(stretch, stress_fem - stress_analytic, '.-')

ax_stress.legend(loc='best').set_draggable(True)
ax_stress.set_ylabel(r'nominal stress $\mathrm{[Pa]}$')
ax_stress.grid()

ax_difference.set_ylabel(r'difference in nominal stress $\mathrm{[Pa]}$')


ax_difference.set_xlabel(r'stretch $\mathrm{[-]}$')
ax_difference.grid()
plt.tight_layout()
plt.show()

def stress_strain(
out, problem, _state, order=1, global_stress=None,
global_displacement=None, **_):
"""
Compute the stress and the strain and add them to the output.

Parameters
----------
out : dict
Holds the results of the finite element computation.
problem : sfepy.discrete.Problem
order : int
The approximation order of the displacement field.
(continues on next page)

1.5. Examples 321


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


global_displacement
Total displacement for each time step, current value will be appended.
global_stress
The true (Cauchy) stress for each time step, current value will be
appended.

Returns
-------
out : dict
"""
strain = problem.evaluate(
'dw_tl_he_genyeoh.%d.Omega(m1.par, v, u)' % (2*order),
mode='el_avg', term_mode='strain', copy_materials=False)

out['green_strain'] = Struct(
name='output_data', mode='cell', data=strain, dofs=None)

stress_1 = problem.evaluate(
'dw_tl_he_genyeoh.%d.Omega(m1.par, v, u)' % (2*order),
mode='el_avg', term_mode='stress', copy_materials=False)
stress_p = problem.evaluate(
'dw_tl_bulk_pressure.%d.Omega(v, u, p)' % (2*order),
mode='el_avg', term_mode='stress', copy_materials=False)
stress = stress_1 + stress_p

out['stress'] = Struct(
name='output_data', mode='cell', data=stress, dofs=None)

global_stress.append(stress[0, 0, 0, 0])
global_displacement.append(get_displacement(
problem.ts, np.array([[1., 0, 0]]))[0])

return out

def main(cli_args):
dims = parse_argument_list(cli_args.dims, float)
shape = parse_argument_list(cli_args.shape, int)
centre = parse_argument_list(cli_args.centre, float)
material_parameters = parse_argument_list(cli_args.material_parameters,
float)
order = cli_args.order

ts_vals = cli_args.ts.split(',')
ts = {
't0' : float(ts_vals[0]), 't1' : float(ts_vals[1]),
'n_step' : int(ts_vals[2])}

do_plot = cli_args.plot

### Mesh and regions ###


mesh = gen_block_mesh(
dims, shape, centre, name='block', verbose=False)
(continues on next page)

322 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


domain = FEDomain('domain', mesh)

omega = domain.create_region('Omega', 'all')

lbn, rtf = domain.get_mesh_bounding_box()


box_regions = define_box_regions(3, lbn, rtf)
regions = dict([
[r, domain.create_region(r, box_regions[r][0], box_regions[r][1])]
for r in box_regions])

### Fields ###


scalar_field = Field.from_args(
'fu', np.float64, 'scalar', omega, approx_order=order-1)
vector_field = Field.from_args(
'fv', np.float64, 'vector', omega, approx_order=order)

u = FieldVariable('u', 'unknown', vector_field, history=1)


v = FieldVariable('v', 'test', vector_field, primary_var_name='u')
p = FieldVariable('p', 'unknown', scalar_field, history=1)
q = FieldVariable('q', 'test', scalar_field, primary_var_name='p')

### Material ###


coefficient, exponent = material_parameters
m_1 = Material(
'm1', par=[coefficient, exponent],
)

### Boundary conditions ###


x_sym = EssentialBC('x_sym', regions['Left'], {'u.0' : 0.0})
y_sym = EssentialBC('y_sym', regions['Near'], {'u.1' : 0.0})
z_sym = EssentialBC('z_sym', regions['Bottom'], {'u.2' : 0.0})
disp_fun = Function('disp_fun', get_displacement)
displacement = EssentialBC(
'displacement', regions['Right'], {'u.0' : disp_fun})
ebcs = Conditions([x_sym, y_sym, z_sym, displacement])

### Terms and equations ###


integral = Integral('i', order=2*order+1)

term_1 = Term.new(
'dw_tl_he_genyeoh(m1.par, v, u)',
integral, omega, m1=m_1, v=v, u=u)
term_pressure = Term.new(
'dw_tl_bulk_pressure(v, u, p)',
integral, omega, v=v, u=u, p=p)

term_volume_change = Term.new(
'dw_tl_volume(q, u)',
integral, omega, q=q, u=u, term_mode='volume')
term_volume = Term.new(
'dw_integrate(q)',
integral, omega, q=q)
(continues on next page)

1.5. Examples 323


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)

eq_balance = Equation('balance', term_1 + term_pressure)


eq_volume = Equation('volume', term_volume_change - term_volume)
equations = Equations([eq_balance, eq_volume])

### Solvers ###


ls = ScipyDirect({})
nls_status = IndexedStruct()
nls = Newton(
{'i_max' : 20},
lin_solver=ls, status=nls_status
)

### Problem ###


pb = Problem('hyper', equations=equations)
pb.set_bcs(ebcs=ebcs)
pb.set_ics(ics=Conditions([]))
tss = SimpleTimeSteppingSolver(ts, nls=nls, context=pb)
pb.set_solver(tss)

### Solution ###


axial_stress = []
axial_displacement = []
def stress_strain_fun(*args, **kwargs):
return stress_strain(
*args, order=order, global_stress=axial_stress,
global_displacement=axial_displacement, **kwargs)

pb.solve(save_results=True, post_process_hook=stress_strain_fun)

if do_plot:
plot_graphs(
material_parameters, axial_stress, axial_displacement,
undeformed_length=dims[0])

def parse_argument_list(cli_arg, type_fun=None, value_separator=','):


"""
Split the command-line argument into a list of items of given type.

Parameters
----------
cli_arg : str
type_fun : function
A function to be called on each substring of `cli_arg`; default: str.
value_separator : str
"""
if type_fun is None:
type_fun = str
out = [type_fun(value) for value in cli_arg.split(value_separator)]
return out

def parse_args():
(continues on next page)

324 Chapter 1. Documentation


SfePy Documentation, Release version: 2022.1+git.f9873484

(continued from previous page)


"""Parse command line arguments."""
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
'--order', type=int, default=1, help='The approximation order of the '
'displacement field [default: %(default)s]')
parser.add_argument(
'-m', '--material-parameters', default='0.5, 0.9',
help='Material parameters - coefficient and exponent - of a single '
'term of the generalized Yeoh hyperelastic model. '
'[default: %(default)s]')
parser.add_argument(
'--dims', default="1.0, 1.0, 1.0",
help='Dimensions of the block [default: %(default)s]')
parser.add_argument(
'--shape', default='2, 2, 2',
help='Shape (counts of nodes in x, y, z) of the block [default: '
'%(default)s]')
parser.add_argument(
'--centre', default='0.5, 0.5, 0.5',
help='Centre of the block [default: %(default)s]')
parser.add_argument(
'-p', '--plot', action='store_true', default=False,
help='Whether to plot a comparison with analytical formula.')
parser.add_argument(
'-t', '--ts',
type=str, default='0.0,2.0,11',
help='Start time, end time, and number of time steps [default: '
'"%(default)s"]')
return parser.parse_args()

if __name__ == '__main__':
args = parse_args()
main(args)

large_deformation/hyperelastic.py

Description
Nearly incompressible Mooney-Rivlin hyperelastic material model.
Large deformation is described using the total Lagrangian formulation. Models of this kind can be used to model e.g.
rubber or some biological materials.
Find 𝑒 such that:
∫︁ (︁ )︁
𝑆 eff (𝑒) + 𝐾(𝐽 βˆ’ 1) 𝐽𝐢 βˆ’1 : 𝛿𝐸(𝑣) d𝑉 = 0 , βˆ€π‘£ ,
Ξ©(0)

where

1.5. Examples 325


SfePy Documentation, Release version: 2022.1+git.f9873484

𝐹 deformation gradient 𝐹𝑖𝑗 = πœ•π‘‹ πœ•π‘₯𝑖


𝑗
𝐽 det(𝐹 )
𝐢 right Cauchy-Green deformation tensor 𝐢 = 𝐹 𝑇 𝐹
πœ•π‘’
𝐸(𝑒) Green strain tensor 𝐸𝑖𝑗 = 21 ( πœ•π‘‹
πœ•π‘’π‘–
𝑗
+ πœ•π‘‹π‘—π‘– + πœ•π‘’ π‘š πœ•π‘’π‘š
πœ•π‘‹π‘– πœ•π‘‹π‘— )
𝑆 eff (𝑒) effective second Piola-Kirchhoff stress tensor

The effective stress 𝑆 eff (𝑒) is given by:

2 1 4 2
𝑆 eff (𝑒) = πœ‡π½ βˆ’ 3 (𝐼 βˆ’ tr(𝐢)𝐢 βˆ’1 ) + πœ…π½ βˆ’ 3 (tr(𝐢𝐼 βˆ’ 𝐢 βˆ’ ((tr 𝐢)2 βˆ’ tr (𝐢 2 ))𝐢 βˆ’1 ) .
3 6

source code

# -*- coding: utf-8 -*-


r"""
Nearly incompressible Mooney-Rivlin hyperelastic material model.

Large deformation is described using the total Lagrangian formulation.


Models of this kind can be used to model e.g. rubber or some biological
materials.

Find :math:`\ul{u}` such that: