Sfepy Manual
Sfepy Manual
1 Documentation 3
1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 Supported Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.2 Installing SfePy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.3 Using SfePy Docker Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.4 Installing SfePy from Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.5 Testing Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.6 Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.7 Using IPython . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.8 Notes on Multi-platform Python Distributions . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3 Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3.1 Basic SfePy Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3.2 Basic Notions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.3 Running a Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3.4 Example Problem Description File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3.5 Interactive Example: Linear Elasticity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.4 Userβs Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.4.1 Running a Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.4.2 Visualization of Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.4.3 Problem Description File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.4.4 Building Equations in SfePy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.4.5 Term Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.4.6 Solution Postprocessing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.4.7 Probing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
1.4.8 Postprocessing filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
1.4.9 Solvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
1.4.10 Solving Problems in Parallel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
1.4.11 Isogeometric Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
1.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1.5.1 Primer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1.5.2 Using Salome with SfePy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.5.3 Preprocessing: FreeCAD/OpenSCAD + Gmsh . . . . . . . . . . . . . . . . . . . . . . . . . 77
1.5.4 Material Identification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
1.5.5 Mesh parametrization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
1.5.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
1.5.7 Example Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
1.6 Useful Code Snippets and FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
1.6.1 Miscellaneous . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709
1.6.2 Mesh-Related Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711
i
1.6.3 Regions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711
1.6.4 Material Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712
1.7 Theoretical Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
1.7.1 Notes on solving PDEs by the Finite Element Method . . . . . . . . . . . . . . . . . . . . . 713
1.7.2 Implementation of Essential Boundary Conditions . . . . . . . . . . . . . . . . . . . . . . . 715
1.8 Term Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
1.8.1 Term Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
1.8.2 Term Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719
2 Development 747
2.1 General Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
2.2 Possible Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
2.3 Developer Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
2.3.1 Retrieving the Latest Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
2.3.2 SfePy Directory Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749
2.3.3 Exploring the Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 750
2.3.4 How to Contribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751
2.3.5 How to Regenerate Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
2.3.6 How to Implement a New Term . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
2.3.7 Multi-linear Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 762
2.3.8 How To Make a Release . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764
2.3.9 Module Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 767
Bibliography 1213
Index 1219
ii
SfePy Documentation, Release version: 2024.2
β’ genindex
β’ modindex
β’ search
CONTENTS 1
SfePy Documentation, Release version: 2024.2
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
SfePy is known to work on various flavors of recent Linux, Intel-based MacOS and Windows. The release 2019.4 was
the last with Python 2.7 support. SfePy should work with any recent Python 3.x that is supported by NumPy and SciPy.
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: 2024.2
It is only matter of taste to use either native OS Python installation, pip, or any other suitable distribution. On all
supported platforms we could recommend multi-platform scientific Python distributions Anaconda as easy-to-use,
stable and up-to-date Python distribution with all the required dependencies (including pre-built sfepy package). See
also Notes on Multi-platform Python Distributions for further details.
On different platforms the following options can be recommended:
β’ Linux: Anaconda, OS native installation, if available, or pip.
β’ macOS: Anaconda.
β’ Windows: free versions of commercial scientific Python distributions Anaconda or Enthought Deployment Man-
ager . In addition a completely free open-source portable distribution WinPython can be used.
Besides the classical installation we also provide official Docker images with ready-to-run Anaconda and SfePy instal-
lation.
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 all-in-one image is:
β’ sfepy/sfepy-desktop - an Ubuntu based container containing a full desktop environment in officially supported
flavors accessible via any modern web browser.
For available runtime options and further information see sfepy-docker project on Github.
The latest stable release can be obtained from the download page. Otherwise, download the development version of
the code from SfePy git repository:
In case you wish to use a specific release instead of the latest master version, use:
4 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
git tag -l
to see the available releases - the release tags have form release_<year>.<int>.
See the download page for additional download options.
Requirements
1.2. Installation 5
SfePy Documentation, Release version: 2024.2
β’ If doxygen is installed, the documentation of data structures and functions can be automatically generated by
running:
After a successful compilation, SfePy can be used in-place. However, the the sfepy-* commands, such as
sfepy-run are only available after installing the package. Their functionality can be accessed by invoking
directly the corresponding scripts in sfepy/scripts/.
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):
pip install .
β’ local:
pip install -e .
The editable install allows working in-place and at the same time the sfepy-* commands are available.
If all went well, proceed with Testing Installation.
6 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
After building and/or installing SfePy you should check if all the functions are working properly by running the auto-
mated tests.
or:
when working in Anaconda. If SfePy was installed, it can be tested using the command:
sfepy-test
sfepy-test --output-dir=output-tests
in the SfePy top-level directory in case of the in-place build and anywhere else when testing the installed package.
Additional pytest options can be passed as arguments to sfepy.test(), for example:
python -c "import sfepy; sfepy.test('-vv', '--durations=0', '-m not slow', '-k test_
Λβassembling.py')"
See pytest usage instructions for other options and usage patterns.
To test an in-place build (e.g. in a cloned git repository), the following simpler command can be used in the sources
top-level directory:
which will also add the current directory to sys.path. If the top-level directory is already in sys.path (e.g. using
export PYTHONPATH=.), the simplest way of invoking pytest is:
pytest sfepy/tests
pytest -v sfepy/tests/test_assembling.py
1.2. Installation 7
SfePy Documentation, Release version: 2024.2
1.2.6 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:
Then re-run your code and report the output to the SfePy mailing list.
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:
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...
#
]
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
Anaconda
8 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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:
Once the conda-forge channel has been enabled, SfePy can be installed with:
It is possible to list all of the versions of SfePy available on your platform with:
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:
or try:
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.3 Tutorial
1.3. Tutorial 9
SfePy Documentation, Release version: 2024.2
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.
This section introduces the basics of running SfePy from the command line, assuming it was properly installed, see
Installation.
The command sfepy-run is the most basic starting point in SfePy. It can be used to run declarative problem description
files (see below) as follows:
sfepy-run <problem_description_file>
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.
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 sec-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. . .
10 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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
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.
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 sec-
problem-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 sfepy-run as described above. The
successful execution of the command creates output file cylinder.vtk in the SfePy top-level directory.
β’ The sfepy-view command can be used for quick postprocessing and visualization of the SfePy output files. It
requires pyvista installed on your system.
β’ As a simple example, try:
sfepy-view cylinder.vtk
β’ The following interactive 3D window should display:
1.3. Tutorial 11
SfePy Documentation, Release version: 2024.2
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
ππ
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.
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'),
}
12 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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.
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}),
}
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 βπ’ β πΊπ’.
1.3. Tutorial 13
SfePy Documentation, Release version: 2024.2
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:
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.
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)
Ξ© Ξ©
14 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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
ipython
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.
Next we define the actual finite element approximation using the Field class.
Using the field fu, we can define both the unknown variable π’ and the test variable π£.
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]π .
1.3. Tutorial 15
SfePy Documentation, Release version: 2024.2
One more thing needs to be defined β the numerical quadrature that will be used to integrate each term over its domain.
Now we are ready to define the two terms and build the equations.
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.
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 [31]: ls = ScipyDirect({})
In [32]: nls_status = IndexedStruct()
In [33]: nls = Newton({}, lin_solver=ls, status=nls_status)
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')
16 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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.
This
sfepy-view linear_elasticity.vtk -2
1.3. Tutorial 17
SfePy Documentation, Release version: 2024.2
The default view is not very fancy. Let us show the displacements by shifting the mesh. Close the previous window
and do
18 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
1 #!/usr/bin/env python
2 """
3 Linear elasticity example using the imperative API.
4 """
5 from argparse import ArgumentParser
6 import numpy as nm
7
8 import sys
9 sys.path.append('.')
10
21
28 return val
29
30
31 def main():
32 from sfepy import data_dir
33
34 parser = ArgumentParser()
35 parser.add_argument('--version', action='version', version='%(prog)s')
36 options = parser.parse_args()
37
1.3. Tutorial 19
SfePy Documentation, Release version: 2024.2
62 t1 = Term.new('dw_lin_elastic(m.D, v, u)',
63 integral, omega, m=m, v=v, u=u)
64 t2 = Term.new('dw_volume_lvf(f.val, v)', integral, omega, f=f, v=v)
65 eq = Equation('balance', t1 + t2)
66 eqs = Equations([eq])
67
74 ls = ScipyDirect({})
75
76 nls_status = IndexedStruct()
77 nls = Newton({}, lin_solver=ls, status=nls_status)
78
79 pb = Problem('elasticity', equations=eqs)
80 pb.save_regions_as_groups('regions')
81
82 pb.set_bcs(ebcs=Conditions([fix_u, shift_u]))
83
84 pb.set_solver(nls)
85
86 status = IndexedStruct()
87 variables = pb.solve(status=status)
88
92 pb.save_state('linear_elasticity.vtk', variables)
93
94
95 if __name__ == '__main__':
96 main()
20 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
The following should be run in the top-level directory of the SfePy source tree after installing the sfepy package. See
Installation for installation instructions.
Basic Usage
β’ sfepy-run sfepy/examples/diffusion/poisson_short_syntax.py
β Creates cylinder.vtk
β’ sfepy-run sfepy/examples/navier_stokes/stokes.py
β Creates channels_symm944t.vtk
Running Tests
Most of the example problems in the sfepy/examples directory can be computed by the sfepy-run command.
Additional (stand-alone) examples can be run directly, e.g.:
python sfepy/examples/large_deformation/compare_elastic_materials.py
Common Tasks
β’ Run a simulation:
sfepy-run sfepy/examples/diffusion/poisson_short_syntax.py
sfepy-run sfepy/examples/diffusion/poisson_short_syntax.py -o some_results # ->β£
Λβproduces some_results.vtk
sfepy-run --list=terms
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:
The simulation should continue from the next time step. Verify that by running:
sfepy-run sfepy/examples/large_deformation/balloon.py
Quick visualisation of the SfePy results can be done by sfepy.scripts.resview.py command, which uses PyVista
visualization toolkit (need to be installed).
For example, results of a stationary Navier-Stokes flow simulation:
sfepy-view navier_stokes.vtk
produces:
22 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
Using:
the output is split into plots plot:0 and plot:1, where these plots contain:
β’ plot:0: field p, 5 isosurfaces, mesh edges switched on, the surface opacity set to 0.2;
β’ plot:1: vector field u streamlines, the surface opacity set to 0.2;
The actual camera position can be printed by pressing the βcβ key. The above figures use the following values:
--camera-position="-0.236731,-0.352216,0.237044,0.094458,0.0292563,0.0385116,0.266969,0.
Λβ252278,0.930099"
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.
24 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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
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:
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]):
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.
26 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
β’ The cell kind is the most general and should be used with cell 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 (facet 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 (cell integration domains, facet domains, etc.)
and could be used 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.
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.
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
28 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
return flag
functions = {
'get_ball' : (get_ball,),
}
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 = {
<name> : (<data_type>, <shape>, <region_name>, <approx_order>,
[<space>, <poly_space_base>])
}
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)
β’ <space> is the function space
β’ <poly_space_base> is the basis of the FE (usually polynomial) space
Example: scalar P1 elements in 2D on a region Omega:
fields = {
'temperature' : ('real', 1, 'Omega', 1),
}
The implemented combinations of spaces and bases are listed below, the space column corresponds to <space>, the
basis column to <poly_space_base> and region type to the field region type.
Table 1: Fields
space basis region kind description
H1 bernstein cell, facet Bernstein basis approximation with positive-only basis function
values.
H1 iga cell Bezier extraction based NURBS approximation for isogeometric
analysis.
H1 lagrange cell, facet Lagrange basis nodal approximation.
H1 lagrange_discontinuous cell The C0 constant-per-cell approximation.
H1 lobatto cell Hierarchical basis approximation with Lobatto polynomials.
H1 sem cell, facet Spectral element method approximation.
H1 serendipity cell, facet Lagrange basis nodal serendipity approximation with order <= 3.
H1 shell10x cell The approximation for the shell10x element.
L2 constant cell, facet The L2 constant-in-a-region approximation.
DG legendre_discontinuous cell Discontinuous Galerkin method approximation with Legendre
basis.
Variables
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
30 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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),
}
The essential boundary conditions set values of DOFs in some regions, while the constraints constrain or transform
values of DOFs in some regions.
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
mtx = rotation_matrix2d(angle)
vec_rotated = nm.dot(vec, mtx)
return displacement
functions = {
'rotate_yz' : (rotate_yz,),
}
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'),
}
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;
32 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
lcbcs = {
'shifted' : (('Left', 'Right'),
{'u1.all' : 'u2.all'},
'match_y_line', 'shifted_periodic',
'get_shift'),
'mean' : ('Middle', {'u1.all' : None}, None, 'integral_mean_value'),
}
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.
}
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:
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).
Examples
equations = {
'Temperature' : """dw_laplace.i.Omega( coef.val, s, t ) = 0"""
}
34 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
equations = {
'Temperature' : """dw_laplace.2.Omega( coef.val, s, t ) = 0"""
}
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""",
}
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
functions = {
'get_circle' : (get_circle,),
}
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'}),
}
36 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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:
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:
functions = {
'get_pars1' : (lambda ts, coors, mode=None, **kwargs:
get_pars_special(ts, coors, mode,
extra_arg='hello!'),),
}
if mode == 'special':
val = coors[:,1]
val.shape = (coors.shape[0], 1, 1)
out['y_coor'] = val
return out
functions = {
'get_pars_both' : (get_pars_both,),
}
variables = {
'p' : ('parameter field', 'temperature',
{'setter' : 'get_load_variable'}),
}
functions = {
'get_load_variable' : (get_load_variable,)
}
38 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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',
# save a restart file for each time step, only the last computed time
# step restart file is kept.
'save_restart' : -1,
β’ post_process_hook enables computing derived quantities, like stress or strain, from the primary unknown
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
40 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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:
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.
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.
β’ β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.
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):
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
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
(continues on next page)
42 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
return out
return out
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
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.py
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.
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:]
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)
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.
44 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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.central_difference: Solve elastodynamics problems by the explicit central difference 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 explicit velocity-Verlet method.
See sfepy.solvers.ts_solvers for available time-stepping solvers and their options.
The following time step controllers are available:
β’ tsc.ed_basic: Adaptive time step I-controller for elastodynamics.
β’ tsc.ed_linear: Adaptive time step controller for elastodynamics and linear problems.
β’ tsc.ed_pid: Adaptive time step PID controller for elastodynamics.
β’ tsc.fixed: Fixed (do-nothing) time step controller.
β’ tsc.time_sequence: Given times sequence time step controller.
See sfepy.solvers.ts_controllers for available time step controllers 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.
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.cholesky: Interface to scikit-sparse.cholesky solver.
β’ 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.rmm: Special solver for explicit transient elastodynamics.
β’ 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.
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.
46 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
Optimization Solvers
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.
β’ 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.
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 cell terms work
β’ term integration over the whole domain as well as a cell subdomain
β’ simple linearization (output file generation) based on sampling the results with uniform parametric vectors
β’ basic domain generation with sfepy/scripts/gen_iga_patch.py based on igakit
The following is not implemented yet:
β’ tests
β’ theoretical convergence rate verification
β’ facet terms
β’ other boundary conditions
β’ proper (adaptive) linearization for post-processing
β’ support for multiple NURBS patches
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.
48 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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.
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
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
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.
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)
ππ‘π·
50 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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.
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.
1.5. Examples 51
SfePy Documentation, Release version: 2024.2
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.
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
52 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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`.
.. 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.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
options = {
'output_dir' : output_dir,
}
regions = {
'Omega' : 'all',
(continues on next page)
1.5. Examples 53
SfePy Documentation, Release version: 2024.2
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'),
}
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'),
(continues on next page)
54 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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:
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:
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.
1.5. Examples 55
SfePy Documentation, Release version: 2024.2
Running SfePy
One option to solve the problem is to run the sfepy-run` command: from the command line:
sfepy-run its2D_1.py
Note: For the purpose of this tutorial it is assumed that the problem description file (its2D_1.py) is in the current
directory. 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 to quickly view the solution. To run this script you need to have pyvista installed. From the
command line issue the following (assuming the correct paths):
sfepy-view its2D.vtk -2 -e
The sfepy-view command 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.
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`.
.. math::
(continues on next page)
56 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
where
.. math::
D_{ijkl} = \mu (\delta_{ik} \delta_{jl}+\delta_{il} \delta_{jk}) +
\lambda \ \delta_{ij} \delta_{kl}
\;.
"""
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',
copy_materials=False)
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):
sfepy-run its2D_2.py
sfepy-view its2D.vtk -2 --max-plots 2
In addition to the node displacements, the VTK output shown below now also includes the stresses and strains averaged
in the elements:
1.5. Examples 57
SfePy Documentation, Release version: 2024.2
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.
In addition to solving problems using the sfepy-run command you can also run SfePy interactively (we will use IPython
interactive shell in following examples).
In the SfePy top-level directory run
ipython
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 [7]: u
Out[7]:
array([[ 0. , 0. ],
[ 0.37376671, 0. ],
[ 0. , -1.65318152],
(continues on next page)
58 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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]])
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
(continues on next page)
1.5. Examples 59
SfePy Documentation, Release version: 2024.2
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 shape of the matrix, temporarily remove the imposed boundary conditions:
In [14]: pb.remove_bcs()
Note that the matrix is reallocated, so it contains only zeros at this moment.
Now we can calculate the force vector π :
In [15]: f = pb.evaluator.eval_residual(u)
ValueError: shape mismatch: value array of shape (55,2) could not be broadcast toβ£
Λβindexing result of shape (110,)
60 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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:
Great, we have an almost zero residual vertical load or force apparent at node 2 i.e. -1.13686838e-13 Newton. Note
that these relatively very small numbers can be slightly different, depending on the linear solver kind and version.
Let us now check the stress at node 0, the centre of the specimen.
1.5. Examples 61
SfePy Documentation, Release version: 2024.2
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`.
.. 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 print_function
from __future__ import absolute_import
from sfepy.examples.linear_elasticity.its2D_1 import *
gdata = geometry_data['2_3']
nc = len(gdata.coors)
# Point load.
mat = pb.get_materials()['Load']
P = 2.0 * mat.get_data('special', 'val')[1]
62 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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]
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
(continues on next page)
1.5. Examples 63
SfePy Documentation, Release version: 2024.2
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)
64 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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`.
.. 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 *
import os
from six.moves import range
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')
1.5. Examples 65
SfePy Documentation, Release version: 2024.2
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)
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
66 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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:
sfepy-run 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.
The probing function also generates previews of the mesh with the probe paths.
1.5. Examples 67
SfePy Documentation, Release version: 2024.2
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
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
"""
from __future__ import absolute_import
(continues on next page)
68 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
import numpy as nm
import matplotlib.pyplot as plt
def gen_lines(problem):
"""
Define two line probes.
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))
1.5. Examples 69
SfePy Documentation, Release version: 2024.2
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)
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)
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',
}
def main():
from sfepy import data_dir
(continues on next page)
70 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
parser = ArgumentParser(description=__doc__,
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('--version', action='version', version='%(prog)s')
parser.add_argument('--young', metavar='float', type=float,
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('-p', '--probe',
action="store_true", dest='probe',
default=False, help=helps['probe'])
options = parser.parse_args()
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)
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))
1.5. Examples 71
SfePy Documentation, Release version: 2024.2
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])
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)
gdata = geometry_data['2_3']
nc = len(gdata.coors)
72 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
if options.probe:
# Probe the solution.
probes, labels = gen_lines(pb)
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)
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)
if __name__ == '__main__':
main()
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:
1.5. Examples 73
SfePy Documentation, Release version: 2024.2
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.
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).
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:
74 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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>.
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.
Mesh specification
The first thing we have to do is tell SfePy to use our new mesh. Change the line
to
filename_mesh = 'Mesh_Partition_Hexa.med'
1.5. Examples 75
SfePy Documentation, Release version: 2024.2
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),
}
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}),
}
76 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
Save your changes to linear_elastic.py. Now itβs time to run the SfePy calculation. In your <work_dir> in your
terminal type:
sfepy-run 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:
You should get the plot with the deformed and undeformed meshs. 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!
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 77
SfePy Documentation, Release version: 2024.2
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
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
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:
78 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
Create a cylinder, make a polar array of the cylinder objects and subtract it from the previous result:
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
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 79
SfePy Documentation, Release version: 2024.2
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);
11 };
(continues on next page)
80 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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 81
SfePy Documentation, Release version: 2024.2
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:
By converting the MSH file into the VTK format using sfepy-convert:
the surface elements are discarded and only the volumetric mesh is preserved.
82 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
that can be described by this Gmsh code, the mesh generator can be called as follows:
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:
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:
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 sfepy-convert 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.5. Examples 83
SfePy Documentation, Release version: 2024.2
The handle and shank are exported to the STEP file as two separated parts:
1 doc.recompute()
2
1 Merge "screwdriver_full.step";
2
84 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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:
In order to extract the cells by the physical groups use the conversion script with --save-per-mat argument:
sfepy-convert --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.
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() {
14 union() {
(continues on next page)
1.5. Examples 85
SfePy Documentation, Release version: 2024.2
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 }
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:
Note: The numbering of objects may vary between FreeCAD, OpenSCAD and Gmsh versions.
86 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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
1.5. Examples 87
SfePy Documentation, Release version: 2024.2
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
88 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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.
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 89
SfePy Documentation, Release version: 2024.2
The resulting values of πΈπ , ππ , πΈπ , ππ can be found at the end of the script output:
So that:
πΈπ = 171.13 GPa
ππ = 3.21
πΈπ = 2.34 GPa
ππ = 0.20
Note: The results may vary across SciPy versions and related libraries.
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.
90 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
SplineBox
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:
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:
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 91
SfePy Documentation, Release version: 2024.2
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:
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.
92 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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:
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
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)
1.5. Examples 93
SfePy Documentation, Release version: 2024.2
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 ππ£π π, βπ .
Ξ© Ξ© Ξππ’π‘ Ξππ
94 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
source code
r"""
Acoustic pressure distribution.
This example shows how to solve a problem in complex numbers, note the
'accoustic_pressure' field definition.
.. 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
1.5. Examples 95
SfePy Documentation, Release version: 2024.2
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)
96 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
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 97
SfePy Documentation, Release version: 2024.2
98 Chapter 1. Documentation
SfePy Documentation, Release version: 2024.2
source code
r"""
Acoustic pressure distribution in 3D.
.. 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 \;.
"""
1.5. Examples 99
SfePy Documentation, Release version: 2024.2
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
k2 = k1 * coef_k
rhoc2 = rhoc1 * coef_r
# 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)
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',
'split_results_by': 'region',
'output_dir': 'output',
}
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,
})
}
acoustics/helmholtz_apartment.py
Description
A script demonstrating the solution of the scalar Helmholtz equation for a situation inspired by the physical problem
of WiFi propagation in an apartment.
The example is an adaptation of the project found here: https://bthierry.pages.math.cnrs.fr/course-fem/projet/
2017-2018/
The boundary conditions are defined as perfect radiation conditions, meaning that on the boundary waves will radiate
outside.
The PDE for this physical process implies to find πΈ(π₯, π‘) for π₯ β β¦ such that:
{οΈ
βπΈ + π 2 π(π₯)2 πΈ = ππ βπ₯ β β¦
ππ πΈ(π₯) β πππ(π₯)πΈ(π₯) = 0 βπ₯ on πβ¦
Usage
The mesh of the appartement and the different material ids can be visualized with the following:
sfepy-view meshes/2d/helmholtz_apartment.vtk -e -2
sfepy-run sfepy/examples/acoustics/helmholtz_apartment.py
Λβ664287,0.730124"
source code
r"""
A script demonstrating the solution of the scalar Helmholtz equation for a
situation inspired by the physical problem of WiFi propagation in an apartment.
The PDE for this physical process implies to find :math:`E(x, t)` for :math:`x
\in \Omega` such that:
.. math::
\left\lbrace
\begin{aligned}
\Delta E+ k^2 n(x)^2 E = f_s && \forall x \in \Omega \\
\partial_n E(x)-ikn(x)E(x)=0 && \forall x \text{ on } \partial \Omega
\end{aligned}
\right.
Usage
-----
The mesh of the appartement and the different material ids can be visualized
with the following::
sfepy-view meshes/2d/helmholtz_apartment.vtk -e -2
sfepy-run sfepy/examples/acoustics/helmholtz_apartment.py
"""
import numpy as nm
from sfepy import data_dir
regions = {
'Omega': 'all',
'Walls': ('cells of group 1'),
'Air': ('cells of group 2'),
'Source': ('cells of group 3'),
'Gamma': ('vertices of surface', 'facet'),
}
# air and walls have different material parameters, hence the 1. and 2.4 factors
materials = {
'material': ({'kn_square': {
'Air': (k * 1.) ** 2,
'Walls': (k * 2.4) ** 2}, },),
'boundary': ({'kn': 1j * k * 2.4},),
'source_func': 'source_func',
}
functions = {
'source_func': (source_func,),
}
fields = {
'electric_field': ('complex', 1, 'Omega', 1),
}
variables = {
'E': ('unknown field', 'electric_field', 1),
'q': ('test field', 'electric_field', 'E'),
}
ebcs = {
}
integrals = {
'i': 2,
}
equations = {
'Helmholtz equation':
"""- dw_laplace.i.Omega(q, E)
+ dw_dot.i.Omega(material.kn_square, q, E)
+ dw_dot.i.Gamma(boundary.kn, q, E)
= dw_integrate.i.Source(source_func.val, q)
"""
}
solvers = {
'ls': ('ls.auto_direct', {}),
'newton': ('nls.newton', {
'i_max': 1,
}),
}
options = {
'refinement_level': 3,
}
acoustics/vibro_acoustic3d.py
Description
Vibro-acoustic problem
3D acoustic domain with 2D perforated deforming interface.
Problem definition - find π (acoustic pressure), π (transversal acoustic velocity), π€ (plate deflection) and π (rotation)
such that:
β«οΈ β«οΈ β«οΈ β«οΈ β«οΈ β«οΈ
2 2 2 + β
π βπ Β· βπ β π ππ + πππ ππ + πππ ππ β πππ (π β π )π = 2πππ π πΒ― , βπ ,
Ξ© Ξ© Ξππ
β«οΈ Ξππ’π‘ Ξ0
β«οΈ β«οΈ Ξππ
βππ π (π+ β πβ ) β π 2 πΉ π π + π2 πΆπ π€ = 0 , βπ ,
Ξ0 Ξ0 Ξ0
β«οΈ β«οΈ β«οΈ β«οΈ
π2 πΆπ§π β π 2 ππ§π€ + βπ§ Β· πΊ Β· βπ€ β π Β· πΊ Β· βπ§ = 0 , βπ§ ,
Ξ0 Ξ0 Ξ0 Ξ0
β«οΈ β«οΈ β«οΈ β«οΈ
βπ 2 π
π Β·π + π·ππππ πππ (π)πππ (π) β π Β· πΊ Β· βπ€ + π Β· πΊ Β· π = 0 , βπ ,
Ξ0 Ξ0 Ξ0 Ξ0
source code
r"""
Vibro-acoustic problem
.. 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 \;,
\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 \;,
def define(sound_speed=343.0,
wave_num=5.5,
p_inc=300,
thickness=0.01,
filename_mesh='/meshes/3d/acoustic_wg.vtk'):
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'),
'Gamma0': ('copy r.Gamma_aux', 'cell', None, {'mesh_dim': 2}),
'Left_': ('vertices in (x < 0.001)', 'edge'),
'Right_': ('vertices in (x > 0.299)', 'edge'),
'Gamma0_Left': ('r.Gamma_aux *v r.Left_', 'edge'),
'Gamma0_Right': ('r.Gamma_aux *v r.Right_', 'edge'),
}
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'),
'theta': ('unknown field', 'rotation', 4),
'nu': ('test field', 'rotation', 'theta'),
}
ebcs = {
'fixed_l': ('Gamma0_Left', {'w.0': 0.0, 'theta.all': 0.0}),
'fixed_r': ('Gamma0_Right', {'w.0': 0.0, 'theta.all': 0.0}),
}
options = {
'split_results_by': 'region',
'output_dir': 'output',
}
functions = {
}
materials = {
'ac': ({
'F': -2.064e+00,
'c': -1.064e+00,
'T': 9.202e-01,
'hG': thickness * 4.5e10 * nm.eye(2),
'hR': thickness * 0.71,
'h3R': thickness**3 / 3.0 * 0.71,
'h3C': thickness**3 / 3.0 * stiffness_from_lame(2, 1e1, 1e0)}, ),
}
equations = {
'eq_p1': """
%e * dw_laplace.5.Omega1(q1, p1)
- %e * dw_dot.5.Omega1(q1, p1)
+ %s * dw_dot.5.GammaIn(q1, p1)
- %s * dw_dot.5.Gamma0_1(q1, tr(Gamma0, g0))
= %s * dw_integrate.5.GammaIn(q1)""" % (c2, w2, 1j * wc,
(continues on next page)
solvers = {
'ls': ('ls.auto_direct', {}),
'nls': ('nls.newton', {
'i_max': 1,
'eps_a': 1e-4,
'eps_r': 1e-6,
})
}
return locals()
dg
dg/advection_1D.py
Description
Transient advection equation in 1D solved using discontinous galerkin method.
ππ
+ π Β· ππ/ππ₯ = 0
ππ‘
π(π‘, 0) = π(π‘, 1)
Usage Examples
Run:
sfepy-run sfepy/examples/dg/advection_1D.py
To view animated results use sfepy/examples/dg/dg_plot_1D.py specifing name of the output in output/ folder,
default is dg/advection_1D:
source code
r"""
Transient advection equation in 1D solved using discontinous galerkin method.
p(t,0) = p(t,1)
Usage Examples
--------------
Run::
sfepy-run sfepy/examples/dg/advection_1D.py
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 = {
(continues on next page)
regions = {
'Omega': 'all',
'Gamma': ('vertices of surface', 'facet'),
'left': ('vertices in x == 0', 'vertex'),
'right': ('vertices in x == 1', 'vertex')
}
fields = {
'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', {})
(continues on next page)
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,
'output_format' : "vtk",
}
functions = {}
def local_register_function(fun):
try:
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)
ics = {
'ic': ('Omega', {'p.0': 'get_ic'}),
}
return locals()
dg/advection_2D.py
Description
Transient advection equation in 2D solved by discontinous Galerkin method.
ππ
+ π Β· ππππ π = 0
ππ‘
Usage Examples
Run:
sfepy-run 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.
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.
Usage Examples
--------------
sfepy-run sfepy/examples/dg/advection_2D.py
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
(continues on next page)
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)],
[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):
r"""
.. :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])
@local_register_function
(continues on next 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,
(continues on next page)
return locals()
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:
sfepy-run sfepy/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.
Based on
Usage Examples
--------------
Run::
sfepy-run sfepy/examples/dg/advection_diffusion_2D.py
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
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))
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]
(continues on next page)
sin = nm.sin
cos = nm.cos
exp = nm.exp
pi = nm.pi
if bc.diff == 0:
if "left" in bc.name:
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)
(continues on next page)
@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',
(continues on next page)
'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'
}
return locals()
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:
sfepy-run 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.
source code
r"""
Burgers equation in 2D solved using discontinous Galerkin method
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::
sfepy-run sfepy/examples/dg/burgers_2D.py
mesh_center = (0, 0)
(continues on next page)
def define(filename_mesh=None,
approx_order=2,
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,)})
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
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'),
(continues on next page)
fields = {
'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,
}
@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:
(continues on next page)
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_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',
(continues on next page)
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()
dg/dg_plot_1D.py
Description
Script for plotting 1D DG FEM data stored in VTK files
source code
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
Script for plotting 1D DG FEM data stored in VTK files
"""
import glob
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
import argparse
if compare:
files = glob(pjoin(folder, "*.vtk"))
n_first = sorted(files)[0].split(".")[-2]
n_last = sorted(files)[-1].split(".")[-2]
plt.figure("Reconstructed solution")
ww_s, xx = reconstruct_legendre_dofs(coors, None, u_end)
ww_e, _ = reconstruct_legendre_dofs(coors, None, u_start)
(continues on next page)
def main(argv):
parser = argparse.ArgumentParser(description='Plotting of 1D DG data in VTK files',
epilog='(c) 2019 T. Zitka , Man-machine'
+' Interaction at NTC UWB')
parser.add_argument("input_name", help="Folder or name of the example in "
+ "output folder with VTK data, file names "
+ "in format <name>.[0-9]*.vtk. If not "
+ "provided asks for the name of the example"
, nargs="?")
if argv is None:
argv = sys.argv[1:]
args = parser.parse_args(argv)
t0 = args.start_time
t1 = args.end_time
cf = args.compare_final
pol = args.polar
if args.input_name is None:
input_name = str(input("Please provide name of the example in output/" +
" folder or full path to data: "))
else:
input_name = args.input_name
if os.path.isdir(input_name):
full_infolder_path = os.path.abspath(input_name)
else:
input_name = pjoin("output", input_name)
if os.path.isdir(input_name):
full_infolder_path = os.path.abspath(input_name)
(continues on next page)
if args.order is None:
order = str(input("Please provide order of approximation, default is 1: "))
if len(order) == 0:
order = 1
else:
try:
order = int(order)
except ValueError:
print("Value {} for order not understood!".format(order))
return
else:
order = args.order
print("Looking for results of order {}".format(order))
full_infolder_path = pjoin(full_infolder_path, str(order))
if not os.path.isdir(full_infolder_path):
print("Input folder with order {} not found".format(full_infolder_path))
return
else:
order = args.order
if __name__ == '__main__':
main(sys.argv[1:])
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
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)",
"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,)})
return fun
Parameters
----------
CFL : float, optional
dt: float, optional
Returns
-------
setup_cfl_condition : callable
expects sfepy.discrete.problem as argument
"""
def setup_cfl_condition(problem):
"""
Sets up CFL condition for problem ts_conf in problem
Parameters
----------
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
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
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}"
.format(ts_conf.cour))
output("Time stepping solver is {}".format(ts_conf.kind))
output("... CFL setup done.")
return setup_cfl_condition
Parameters
----------
max_velo : float
dx : float
approx_order : int
CFL : CFL
Returns
-------
dt : float
"""
order_corr = 1. / (2 * approx_order + 1)
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
Parameters
----------
max_diffusion : float
dx : float
approx_order : int
CFL : float
do_order_corr : bool
Returns
-------
dt : float
"""
if max_diffusion is None:
return 1
if do_order_corr:
order_corr = 1. / (2 * approx_order + 1)
else:
order_corr = 1
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
Parameters
----------
XS : float
(continues on next page)
Returns
-------
mio : UserMeshIO instance
"""
def mesh_hook(mesh, mode):
"""
Generate the 1D mesh.
"""
if mode == 'read':
mio = UserMeshIO(mesh_hook)
return mio
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
(continues on next page)
mio = UserMeshIO(mesh_hook)
return mio
Parameters
----------
clear_format : str
confirm : bool
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
# 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
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):
(continues on next page)
# vvvvvvvvvvvvvvvv #
approx_order = 2
# ^^^^^^^^^^^^^^^^ #
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 |
# -----------------------------
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
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
# ------------------------------
# | 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):
(continues on next page)
# ------------------
# | 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 |
# ---------------------------
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 = TVDRK3StepSolver(tss_conf,
nls=nls, context=pb, verbose=True)
# ---------
# | Solve |
# ---------
pb.set_solver(tss)
state_end = pb.solve()
# ----------
# | Plot 1D|
# ----------
if options.plot:
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
Usage Examples
Run:
sfepy-run 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.
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
(continues on next page)
Usage Examples
--------------
Run::
sfepy-run sfepy/examples/dg/laplace_2D.py
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):
(continues on next page)
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 = {
'p': ('unknown field', 'f', 0, 1),
'v': ('test field', 'f', 'p'),
}
@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)
(continues on next page)
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"
}
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,
}
(continues on next page)
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()
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 , βπ .
Ξ©
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.
.. math::
\int_{\Omega} c \nabla s \cdot \nabla T
= 0
\;, \quad \forall s \;.
"""
from __future__ import absolute_import
from sfepy import data_dir
############# Laplace.
material_1 = {
(continues on next page)
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',
}
'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)
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)
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.
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,
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)""",
solvers = {
'ls': ('ls.scipy_direct', {}),
'newton': ('nls.newton',
{'i_max' : 1,
(continues on next page)
return out
functions = {
'mat_fun': (mat_fun,),
}
options = {
'post_process_hook': 'postproc',
}
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().
View the results using:
source code
r"""
Laplace equation in 1D with a variable coefficient.
.. math::
\int_{\Omega} c(x) \tdiff{s}{x} \tdiff{t}{x}
= 0
\;, \quad \forall s \;,
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'),
}
fields = {
'temperature' : ('real', 1, 'Omega', 1),
(continues on next page)
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.
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:
source code
r"""
Two Laplace equations with multiple linear combination constraints.
.. math::
\int_{\Omega_1} \nabla v_1 \cdot \nabla u_1
= 0
\;, \quad \forall v_1 \;,
options = {
'nls' : 'newton',
(continues on next page)
return val
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'),
}
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,
}),
}
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
β Β· βπ = βπ = 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
sfepy-run sfepy/examples/diffusion/laplace_fluid_2d.py
sfepy-view citroen.vtk -f phi:p0 phi:t50:p0 --2d-view
source code
r"""
A Laplace equation that models the flow of "dry water" around an obstacle
shaped like a Citroen CX.
Description
-----------
.. math::
\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
(continues on next page)