.. highlight:: python
:linenothreshold: 3
.. include:: links.inc
.. _developer_guide:
Developer Guide
===============
.. only:: html
.. contents:: Table of Contents
:local:
:backlinks: top
This section purports to document the *SfePy* internals. It is mainly useful
for those who wish to contribute to the development of *SfePy* and understand
the inner workings of the code.
We use `git`_ to track source code, documentation, examples, and other files
related to the project.
It is not necessary to learn git in order to contribute to *SfePy* but we
strongly suggest you do so as soon as possible  it is an extremely useful tool
not just for writing code, but also for tracking revisions of articles,
Ph.D. theses, books, ... it will also look well in your CV :) It is also much
easier for us to integrate changes that are in form of a github pull request
than in another form.
Retrieving the Latest Code

The first step is to obtain the latest development version of the code from the
`SfePy git repository`_::
git clone git://github.com/sfepy/sfepy.git
For development, it is preferable to build the extension modules in place (see
:ref:`compilation`)::
python setup.py build_ext inplace
On Unixlike systems, you can simply type ``make`` in the toplevel folder to
build inplace.
After the initial compilation, or after making changes, do not forget to run
the tests, see :ref:`testing_installation`.
SfePy Directory Structure

Here we list and describe the directories that are in the main sfepy
directory.
.. listtable:: Top directory structure.
:widths: 10 90
:headerrows: 1
*  name
 description
*  `build/`
 directory created by the build process (generated)
*  `doc/`
 source files of this documentation
*  `meshes/`
 finite element mesh files in various formats shared by the examples
*  `output/`
 default output directory for storing results of the examples
*  `sfepy/`
 the source code including examples and tests
*  `tools/`
 various helper scripts (build, documentation generation etc.)
New users/developers (after going through the :ref:`sectutorial`)
should explore the `sfepy/examples/` directory. For developers, the principal
directory is `sfepy/`, which has the following contents:
.. listtable:: `sfepy/` directory structure.
:widths: 10 80 10
:headerrows: 1
*  name
 description
 fieldspecific
*  `applications/`
 top level application classes (e.g. :class:`PDESolverApp`)

*  `base/`
 common utilities and classes used by most of the other modules

*  `discrete/`
 general classes and modules for describing a discrete problem, taking
care of boundary conditions, degrees of freedom, approximations,
variables, equations, meshes, regions, quadratures, etc.
Discretizationspecific classes are in subdirectories:
 `common/`  common parent classes for discretizationspecific classes
 `fem/`  finite element specific classes
 `iga/`  isogeometric analysis specific classes

*  `examples/`
 the examples using both the declarative and imperative problem
description API

*  `homogenization/`
 the homogenization engine and supporting modules  highly
specialized code, one of the reasons of *SfePy* existence
 *
*  `linalg/`
 linear algebra functions not covered by NumPy and SciPy

*  `mechanics/`
 modules for (continuum) mechanics: elastic constant
conversions, tensor, units utilities, etc.
 *
*  `mesh/`
 some utilities to interface with tetgen and triangle mesh generators

*  `parallel/`
 modules supporting parallel assembling and solution of problems

*  `postprocess/`
 Matplotlib and VTK based postprocessing modules

*  `scripts/`
 the main as well as auxiliary scripts

*  `solvers/`
 interface classes to various internal/external solvers (linear,
nonlinear, eigenvalue, optimization, time stepping)

*  `terms/`
 implementation of the terms (weak formulation integrals), see
:ref:`term_overview`

*  `tests/`
 the tests

The directories in the "fieldspecific" column are mostly interesting
for specialists working in the respective fields.
The `discrete/` is the heart of the code, while the `terms/` contains the
particular integral forms usable to build equations  new term writers
should look there.
Exploring the Code

It is convenient to install IPython (see also :ref:`usingipython`) to have the
tab completion available. Moreover, all SfePy classes can be easily examined by
printing them::
In [1]: from sfepy.discrete.fem import Mesh
In [2]: mesh = Mesh.from_file('meshes/2d/rectangle_tri.mesh')
sfepy: reading mesh [line2, tri3, quad4, tetra4, hexa8] (meshes/2d/rectangle_tri.mesh)...
sfepy: ...done in 0.00 s
In [3]: print(mesh)
Mesh:meshes/2d/rectangle_tri
cmesh:
CMesh: n_coor: 258, dim 2, tdim: 2, n_el 454
descs:
list: ['2_3']
dim:
2
dims:
list: [2]
io:
None
n_el:
454
n_nod:
258
name:
meshes/2d/rectangle_tri
nodal_bcs:
dict with keys: []
We recommend going through the interactive example in the tutorial
:ref:`secinteractiveexamplelinearelasticity` in this way, printing all the
variables.
Another useful tool is the :func:`debug() ` function,
that can be used as follows::
from sfepy.base.base import debug; debug()
Try to use it in the examples with user defined functions to explore their
parameters etc. It works best with IPython installed, as then the tab
completion is available also when debugging.
.. _how_to_contribute:
How to Contribute

Read this section if you wish to contribute some work to the *SfePy* project 
everyone is welcome to contribute. Contributions can be made in a variety of
forms, not just code. Reporting bugs and contributing to the documentation,
tutorials, and examples is in great need!
Below we describe
#. where to report problems or find existing issues and additional development
suggestions
#. what to do to apply changes/fixes
#. what to do after you made your changes/fixes
Reporting problems
^^^^^^^^^^^^^^^^^^
*Reporting a bug is the first way in which to contribute to an open source
project*
Short version: go to the main `SfePy`_ site and follow the links given there.
When you encounter a problem, try searching that site first  an answer may
already be posted in the `SfePy mailing list`_ (to which we suggest you
subscribe...), or the problem might have been added to the `SfePy issues`_.
As is true in any open source project, doing your homework by searching
for existing known problems greatly reduces the burden on the developers by
eliminating duplicate issues. If you find your problem already exists in the
issue tracker, feel free to gather more information and append it to the
issue. In case the problem is not there, create a new issue with proper labels
for the issue type and priority, and/or ask us using the mailing list.
**Note:** A google account (e.g., gmail account) is needed to join the mailing
list. A github account is needed for working with the source code repository
and issues.
**Note:** When reporting a problem, try to provide as much information as
possible concerning the version of *SfePy*, the OS / Linux distribution, and
the versions of *Python*, *NumPy* and *SciPy*, and other prerequisites. The
versions found on your system can be printed by running::
python setup.py help
If you are a new user, please let us know what difficulties you have with this
documentation. We greatly welcome a variety of contributions not limited to
code only.
Contributing changes
^^^^^^^^^^^^^^^^^^^^
**Note:** To avoid duplicating work, it is highly advised that you contact the
developers on the mailing list or create an enhancement issue before starting
work on a nontrivial feature.
Before making any changes, read the :ref:`notes_patches`.
Using git and github
""""""""""""""""""""
The preferred way to contribute to *SfePy* is to fork the main repository on
github, then submit a "pull request" (PR):
#. `Create a github account`_ if you do not already have one.
#. Fork the project repository: click on the "Fork" button near the top of the
`sfepy git repository`_ page. This creates a copy of the repository under
your account on the github server.
#. Clone your fork to your computer::
git clone git@github.com:YourLogin/sfepy.git
#. If you have never used git before, introduce yourself to git and make
(optionally) some handy aliases either in ``.gitconfig`` in your home
directory (global settings for all your git projects), or directly in
``.git/config`` in the repository::
[user]
email = mail@mail.org
name = Name Surname
[color]
ui = auto
interactive = true
[alias]
ci = commit
di = diff colorwords
st = status
co = checkout
#. Create a feature branch to hold your changes::
git checkout b myfeature
Then you can start to make your changes. Do not work in the master branch!
#. Modify some files and use git to track your local changes. The changed
added/modified files can be listed using::
git status
and the changes can be reviewed using::
git diff
A more convenient way of achieving the above is to run::
gitk all
in order to visualize of project history (all branches). There are other
GUIs for this purpose, e.g. ``qgit``. You may need to install those tools,
as they usually are not installed with git by default. Record a set of
changes by::
# schedule some of the changed files for the next commit
git add file1 file2 ...
# an editor will pop up where you should describe the commit
git commit
We recommend ``git gui`` command in case you want to add and commit only
some changes in a modified file.
**Note:** Do not be afraid to experiment  git works with your *local* copy
of the repository, so it is not possible to damage the master repository. It
is always possible to reclone a fresh copy, in case you do something that
is really bad.
#. The commit(s) now reflect changes, but only in your *local* git
repository. To update your github repository with your new commit(s), run::
git push origin myfeature:myfeature
#. Finally, when your feature is ready, and all tests pass, go to the github
page of your sfepy repository fork, and click "Pull request" to send your
changes to the maintainers for review. It is recommended to check that your
contribution complies with the :ref:`notes_patches`.
In the above setup, your origin remote repository points to
``YourLogin/sfepy.git``. If you wish to fetch/merge from the main repository
instead of your forked one, you will need to add another remote to use instead
of origin. The main repository is usually called "upstream". To add it, type::
git remote add upstream https://github.com/sfepy/sfepy.git
To synchronize your repository with the upstream, proceed as follows:
#. Fetch the upstream changes::
git fetch upstream
Never start with ``git pull upstream``!
#. Check the changes of the upstream master branch. You can use ``gitk all``
to visualize all your and remote branches. The upstream master is named
``remotes/upstream/master``.
#. Make sure all your local changes are either committed in a feature branch or
stashed (see ``git stash``). Then reset your master to the upstream master::
git checkout master
git reset hard upstream/master
**Warning** The above will remove all your local commits in the master
branch that are not in ``upstream/master``, and also reset all the changes
in your noncommitted modified files!
Optionally, the reset command can be run conveniently in ``gitk`` by
rightclicking on a commit you want to reset the current branch onto.
#. Optionally, rebase your feature branch onto the upstream master::
git checkout myfeature
git rebase upstream/master
This is useful, for example, when the upstream master contains a change you
need in your feature branch.
For additional information, see, for example, the `gitwash`_ git tutorial, or
its incarnation `NumPy gitwash`_.
.. _notes_patches:
Notes on commits and patches
""""""""""""""""""""""""""""
 Follow our :ref:`coding_style`.
 Do not use lines longer than 79 characters (exception: tables of
values, e.g., quadratures).
 Write descriptive docstrings in correct style, see :ref:`docstrings`.
 There should be one patch for one topic  do not mix unrelated things in one
patch. For example, when you add a new function, then notice a typo in
docstring in a nearby function and correct it, create two patches: one fixing
the docstring, the other adding the new function.
 The commit message and description should clearly state what the patch
does. Try to follow the style of other commit messages. Some interesting
notes can be found at `tbaggery.com`_, namely that the commit message is
better to be written in the present tense: "fix bug" and not "fixed bug".
Without using git
"""""""""""""""""
Without using git, send the modified files to the `SfePy mailing list`_ or
attach them using `gist`_ to the corresponding issue at the `Issues`_ web
page. Do not forget to describe the changes properly, and to follow the spirit
of :ref:`notes_patches` and the :ref:`coding_style`.
.. _coding_style:
Coding style
^^^^^^^^^^^^
All the code in SfePy should try to adhere to python style guidelines, see
`PEP0008`_.
There are some additional recommendations:
 Prefer whole words to abbreviations in public APIs  there is completion
after all. If some abbreviation is needed (*really* too long name), try to
make it as comprehensible as possible. Also check the code for similar
names  try to name things consistently with the existing code. Examples:
 yes: ``equation``, ``transform_variables()``, ``filename``
 rather not: ``eq``, ``transvar()``, ``fname``
 Functions have usually form ``_()`` e.g.: ``save_data()``,
``transform_variables()``, do not use ``data_save()``,
``variable_transform()`` etc.
 Variables like ``V``, ``c``, ``A``, ``b``, ``x`` should be tolerated only
locally when expressing mathematical ideas.
Really minor recommendations:
 Avoid single letter names, if you can:
 not even for loop variables  use e.g. ir, ic, ... instead of i, j for rows
and columns
 not even in generators, as they "leak" (this is fixed in Python 3.x)
These are recommendations only, we will not refuse code just on the ground that
it uses slightly different formatting, as long as it follows the PEP.
Note: some old parts of the code might not follow the PEP, yet. We fix them
progressively as we update the code.
.. _docstrings:
Docstring standard
""""""""""""""""""
We use `sphinx`_ with the `numpydoc`_ extension to generate this
documentation. Refer to the sphinx site for the possible markup constructs.
Basically (with a little tweak), we try to follow the NumPy/SciPy docstring
standard as described in `NumPy documentation guide`_. See also the complete
`docstring example`_. It is exaggerated a bit to show all the
possibilities. Use your common sense here  the docstring should be sufficient
for a new user to use the documented object. A good way to remember the format
is to type::
In [1]: import numpy as nm
In [2]: nm.sin?
in `ipython`. The little tweak mentioned above is the starting newline::
def function(arg1, arg2):
"""
This is a function.
Parameters

arg1 : array
The coordinates of ...
arg2 : int
The dimension ...
Returns

out : array
The resulting array of shape ....
"""
It seems visually better than::
def function(arg1, arg2):
"""This is a function.
Parameters

arg1 : array
The coordinates of ...
arg2 : int
The dimension ...
Returns

out : array
The resulting array of shape ....
"""
When using :math:`\mbox{\LaTeX}` in a docstring, use a raw string::
def function():
r"""
This is a function with :math:`\mbox{\LaTeX}` math:
:math:`\frac{1}{\pi}`.
"""
to prevent Python from interpreting and consuming the backslashes in common
escape sequences like '\\n', '\\f' etc.
.. _how_to_regenerate_documentation:
How to Regenerate Documentation

The following steps summarize how to regenerate this documentation.
#. Install `sphinx`_ and `numpydoc`_. Do not forget to set the path to numpydoc
in site_cfg.py if it is not installed in a standard location for Python
packages on your platform. A recent :math:`\mbox{\LaTeX}` distribution is
required, too, for example `TeX Live`_. Depending on your OS/platform, it
can be in the form of one or several packages.
#. Edit the rst files in `doc/` directory using your favorite text editor  the
ReST format is really simple, so nothing fancy is needed. Follow the
existing files in `doc/`; for reference also check `reStructuredText
Primer`_, `Sphinx Markup Constructs`_ and `docutils reStructuredText`_.
 When adding a new Python module, add a corresponding documentation file
into `doc/src/sfepy/`, where `` should reflect the location of
the module in `sfepy/`.
 Figures belong to `doc/images`; subdirectories can be used.
#. (Re)generate the documentation (assuming GNU make is installed)::
cd doc
make html
#. View it (substitute your favorite browser)::
firefox _build/html/index.html
.. _how_to_implement_a_new_term:
How to Implement a New Term

**Warning** Implementing a new term usually involves C. As Cython is now
supported by our build system, it should not be that
difficult. Pythononly terms are possible as well.
**Note** There is an experimental way (newly from version 2021.1) of
implementing multilinear terms that is much easier than what is described
here, see :ref:`multilinear_terms`.
Notes on terminology
^^^^^^^^^^^^^^^^^^^^
*Volume* refers to the whole domain (in space of dimension :math:`d`), while
*surface* to a subdomain of dimension :math:`d1`, for example a part of the
domain boundary. So in 3D problems volume = volume, surface = surface, while in
2D volume = area, surface = curve.
Introduction
^^^^^^^^^^^^
A term in *SfePy* usually corresponds to a single integral term in (weak)
integral formulation of an equation. Both volume and surface integrals are
supported. There are three types of arguments a term can have:
 *variables*, i.e. the unknown, test or parameter variables declared by the
`variables` keyword, see :ref:`secproblemdescriptionfile`,
 *materials*, corresponding to material and other parameters (functions)
that are known, declared by the `materials` keyword,
 *user data*  anything, but user is responsible for passing them to the
evaluation functions.
SfePy terms are subclasses of :class:`sfepy.terms.terms.Term`. The purpose of a
term is to implement a (vectorized) function that evaluates the term
contribution to residual/matrix and/or evaluates the term integral in elements
of the term region. Many such functions are currently implemented in C, but
some terms are pure Python, vectorized using NumPy.
Evaluation modes
^^^^^^^^^^^^^^^^
A term can support several evaluation modes, as described in
:ref:`term_evaluation`.
Basic attributes
^^^^^^^^^^^^^^^^
A term class should inherit from :class:`sfepy.terms.terms.Term` base
class. The simplest possible term with volume integration and 'weak'
evaluation mode needs to have the following attributes and methods:
 docstring (not really required per se, but we require it);
 `name` attribute  the name to be used in `equations`;
 `arg_types` attribute  the types of arguments the term accepts;
 `integration` attribute, optional  the kind of integral the term
implements, one of `'volume'` (the default, if not given), `'surface'`,
`'surface_extra'` or `'by_region'`;
 `function()` static method  the assembling function;
 `get_fargs()` method  the method that takes term arguments and
converts them to arguments for `function()`.
Argument types
""""""""""""""
The argument types can be ("[_*]" denotes an optional suffix):
 `'material[_*]'` for a material parameter, i.e. any function that can
be can evaluated in quadrature points and that is not a variable;
 `'opt_material[_*]'` for an optional material parameter, that can be left
out  there can be only one in a term and it must be the first argument;
 `'virtual'` for a virtual (test) variable (no value defined), `'weak'`
evaluation mode;
 `'state[_*]'` for state (unknown) variables (have value), `'weak'`
evaluation mode;
 `'parameter[_*]'` for parameter variables (have known value), any
evaluation mode.
Only one `'virtual'` variable is allowed in a term.
Integration kinds
"""""""""""""""""
The integration kinds have the following meaning:
 `'volume'` for volume integral over a region that contains elements;
uses volume element connectivity for assembling;
 `'surface'` for surface integral over a region that contains faces;
uses surface face connectivity for assembling;
 `'surface_extra'` for surface integral over a region that contains
faces; uses volume element connectivity for assembling  this is
needed if full gradients of a variable are required on the boundary;
 `'by_region'`  the integration mode is determined by the region kind,
The term attribute 'surface_integration' allows to set `'surface_extra'`
integration for surface regions.
`function()`
""""""""""""
The `function()` static method has always the following arguments::
out, *args
where `out` is the already preallocated output array (change it in
place!) and `*args` are any other arguments the function requires. These
function arguments have to be provided by the `get_fargs()` method. The
function returns zero `status` on success, nonzero on failure.
The `out` array has shape `(n_el, 1, n_row, n_col)`, where `n_el` is the
number of elements and `n_row`, `n_col` are matrix dimensions
of the value on a single element.
`get_fargs()`
"""""""""""""
The `get_fargs()` method has always the same structure of arguments:
 positional arguments corresponding to `arg_types` attribute:
 example for a typical weak term:
 for::
arg_types = ('material', 'virtual', 'state')
the positional arguments are::
material, virtual, state
 keyword arguments common to all terms::
mode=None, term_mode=None, diff_var=None, **kwargs
here:
 `mode` is the actual evaluation mode, default is `'eval'`;
 `term_mode` is an optional term submode influencing what the term
should return (example: `dw_tl_he_neohook` term has 'strain' and
'stress' evaluation submodes);
 `diff_var` is taken into account in the `'weak'` evaluation mode. It
is either `None` (residual mode) or a name of variable with respect
to differentiate to (matrix mode);
 `**kwargs` are any other arguments that the term supports.
The `get_fargs()` method returns arguments for `function()`.
Additional attributes
^^^^^^^^^^^^^^^^^^^^^
These attributes are used mostly in connection with the
`tests/test_term_call_modes.py` test for automatic testing of term calls.
 `arg_shapes` attribute  the possible shapes of term arguments;
 `geometries` attribute  the list of reference element geometries that the
term supports;
 `mode` attribute  the default evaluation mode.
Argument shapes
"""""""""""""""
The argument shapes are specified using a dict of the following form::
arg_shapes = {'material' : 'D, D', 'virtual' : (1, 'state'),
'state' : 1, 'parameter_1' : 1, 'parameter_2' : 1}
The keys are the argument types listed in the `arg_types` attribute, for
example::
arg_types = (('material', 'virtual', 'state'),
('material', 'parameter_1', 'parameter_2'))
The values are the shapes containing either integers, or 'D' (for space
dimension) or 'S' (symmetric storage size corresponding to the space
dimension). For materials, the shape is a string `'nr, nc'` or a single value,
denoting a specialvalued term, or `None` denoting an optional material that is
left out. For state and parameter variables, the shape is a single value. For
virtual variables, the shape is a tuple of a single shape value and a
name of the corresponding state variable; the name can be `None`.
When several alternatives are possible, a list of dicts can be used. For
convenience, only the shapes of arguments that change w.r.t. a previous dict
need to be included, as the values of the other shapes are taken from the
previous dict. For example, the following corresponds to a case, where an
optional material has either the shape (1, 1) in each point, or is left out::
arg_types = ('opt_material', 'parameter')
arg_shapes = [{'opt_material' : '1, 1', 'parameter' : 1},
{'opt_material' : None}]
Geometries
""""""""""
The default that most terms use is a list of all the geometries::
geometries = ['2_3', '2_4', '3_4', '3_8']
In that case, the attribute needs not to be define explicitly.
.. _original_term_examples:
Examples
^^^^^^^^
Let us now discuss the implementation of a simple weak term
`dw_integrate` defined as :math:`\int_{\cal{D}} c q`, where :math:`c` is a
weight (material parameter) and :math:`q` is a virtual variable. This term is
implemented as follows::
class IntegrateOperatorTerm(Term):
r"""
Integral of a test function weighted by a scalar function
:math:`c`.
:Definition:
.. math::
\int_{\cal{D}} q \mbox{ or } \int_{\cal{D}} c q
:Arguments:
 material : :math:`c` (optional)
 virtual : :math:`q`
"""
name = 'dw_integrate'
arg_types = ('opt_material', 'virtual')
arg_shapes = [{'opt_material' : '1, 1', 'virtual' : (1, None)},
{'opt_material' : None}]
integration = 'by_region'
@staticmethod
def function(out, material, bf, geo):
bf_t = nm.tile(bf.transpose((0, 1, 3, 2)), (out.shape[0], 1, 1, 1))
bf_t = nm.ascontiguousarray(bf_t)
if material is not None:
status = geo.integrate(out, material * bf_t)
else:
status = geo.integrate(out, bf_t)
return status
def get_fargs(self, material, virtual,
mode=None, term_mode=None, diff_var=None, **kwargs):
assert_(virtual.n_components == 1)
geo, _ = self.get_mapping(virtual)
return material, geo.bf, geo
 lines 214: the docstring  always write one!
 line 15: the name of the term, that can be referred to in equations;
 line 16: the argument types  here the term takes a single material
parameter, and a virtual variable;
 lines 1718: the possible argument shapes
 line 19: the integration mode is choosen according to a given domain
 lines 2129: the term function
 its arguments are:
 the output array `out`, already having the required shape,
 the material coefficient (array) `mat` evaluated in physical
quadrature points of elements of the term region,
 a base function (array) `bf` evaluated in the quadrature points of
a reference element and
 a reference element (geometry) mapping `geo`.
 line 23: transpose the base function and tile it so that is has
the correct shape  it is repeated for each element;
 line 24: ensure C contiguous order;
 lines 2528: perform numerical integration in C  `geo.integrate()`
requires the C contiguous order;
 line 29: return the status.
 lines 3136: prepare arguments for the function above:
 line 33: verify that the variable is scalar, as our implementation
does not support vectors;
 line 34: get reference element mapping corresponding to the virtual
variable;
 line 36: return the arguments for the function.
A more complex term that involves an unknown variable and has two call modes,
is `dw_s_dot_mgrad_s`, defined as :math:`\int_{\Omega} q \ul{y} \cdot \nabla p`
in the`'grad_state'` mode or :math:`\int_{\Omega} p \ul{y} \cdot \nabla q` in
the `'grad_virtual'` mode, where :math:`\ul{y}` is a vector material parameter,
:math:`q` is a virtual variable, and :math:`p` is a state variable::
class ScalarDotMGradScalarTerm(Term):
r"""
Volume dot product of a scalar gradient dotted with a material vector
with a scalar.
:Definition:
.. math::
\int_{\Omega} q \ul{y} \cdot \nabla p \mbox{ , }
\int_{\Omega} p \ul{y} \cdot \nabla q
:Arguments 1:
 material : :math:`\ul{y}`
 virtual : :math:`q`
 state : :math:`p`
:Arguments 2:
 material : :math:`\ul{y}`
 state : :math:`p`
 virtual : :math:`q`
"""
name = 'dw_s_dot_mgrad_s'
arg_types = (('material', 'virtual', 'state'),
('material', 'state', 'virtual'))
arg_shapes = [{'material' : 'D, 1',
'virtual/grad_state' : (1, None),
'state/grad_state' : 1,
'virtual/grad_virtual' : (1, None),
'state/grad_virtual' : 1}]
modes = ('grad_state', 'grad_virtual')
@staticmethod
def function(out, out_qp, geo, fmode):
status = geo.integrate(out, out_qp)
return status
def get_fargs(self, mat, var1, var2,
mode=None, term_mode=None, diff_var=None, **kwargs):
vg1, _ = self.get_mapping(var1)
vg2, _ = self.get_mapping(var2)
if diff_var is None:
if self.mode == 'grad_state':
geo = vg1
bf_t = vg1.bf.transpose((0, 1, 3, 2))
val_qp = self.get(var2, 'grad')
out_qp = bf_t * dot_sequences(mat, val_qp, 'ATB')
else:
geo = vg2
val_qp = self.get(var1, 'val')
out_qp = dot_sequences(vg2.bfg, mat, 'ATB') * val_qp
fmode = 0
else:
if self.mode == 'grad_state':
geo = vg1
bf_t = vg1.bf.transpose((0, 1, 3, 2))
out_qp = bf_t * dot_sequences(mat, vg2.bfg, 'ATB')
else:
geo = vg2
out_qp = dot_sequences(vg2.bfg, mat, 'ATB') * vg1.bf
fmode = 1
return out_qp, geo, fmode
Only interesting differences with respect to the previous example will by
discussed:
 the argument types and shapes (lines 2329) have to be specified for all the
call modes (line 30)
 the term function (lines 3235) just integrates the element contributions, as
all the other calculations are done by the `get_fargs()` function.
 the `get_fargs()` function (lines 3768) contains:
 residual computation (lines 4354) for both modes
 matrix computation (lines 5766) for both modes
Concluding remarks
^^^^^^^^^^^^^^^^^^
This is just a very basic introduction to the topic of new term
implementation. Do not hesitate to ask the `SfePy mailing list`_, and look at
the source code of the already implemented terms.
.. _multilinear_terms:
Multilinear Terms

*tentative documentation, the enriched einsum notation is still in flux*
Multilinear terms can be implemented simply by using the following enriched
einsum notation:
.. listtable:: The enriched einsum notation for defining multilinear terms.
:widths: 10 55 35
:headerrows: 1
*  symbol
 meaning
 example
*  ``0``
 scalar
 :math:`p`
*  ``i``
 :math:`i`th vector component
 :math:`u_i`
*  ``i.j``
 gradient: derivative of :math:`i`th vector component w.r.t.
:math:`j`th coordinate component
 :math:`\pdiff{u_i}{x_j}`
*  ``i:j``
 symmetric gradient
 :math:`\frac{1}{2} (\pdiff{u_i}{x_j} + \pdiff{u_j}{x_i})`
*  ``s(i:j)>I``
 vector storage of symmetric second order tensor, :math:`I` is the vector
component
 Cauchy strain tensor :math:`e_{ij}(\ul{u})`
The examples below present the new way of implementing the terms shown in
the original :ref:`original_term_examples`, using
:class:`sfepy.terms.terms_multilinear.ETermBase`.
Examples
^^^^^^^^
 `de_integrate` defined as :math:`\int_\Omega c q`, where :math:`c` is
a weight (material parameter) and :math:`q` is a virtual variable::
class EIntegrateOperatorTerm(ETermBase):
r"""
Volume and surface integral of a test function weighted by a scalar
function :math:`c`.
:Definition:
.. math::
\int_{\cal{D}} q \mbox{ or } \int_{\cal{D}} c q
:Arguments:
 material : :math:`c` (optional)
 virtual : :math:`q`
"""
name = 'de_integrate'
arg_types = ('opt_material', 'virtual')
arg_shapes = [{'opt_material' : '1, 1', 'virtual' : (1, None)},
{'opt_material' : None}]
def get_function(self, mat, virtual, mode=None, term_mode=None,
diff_var=None, **kwargs):
if mat is None:
fun = self.make_function(
'0', virtual, diff_var=diff_var,
)
else:
fun = self.make_function(
'00,0', mat, virtual, diff_var=diff_var,
)
return fun
 `de_s_dot_mgrad_s` defined as :math:`\int_{\Omega} q \ul{y} \cdot \nabla p`
in the`'grad_state'` mode or :math:`\int_{\Omega} p \ul{y} \cdot \nabla q` in
the `'grad_virtual'` mode, where :math:`\ul{y}` is a vector material
parameter, :math:`q` is a virtual variable, and :math:`p` is a state
variable::
class EScalarDotMGradScalarTerm(ETermBase):
r"""
Volume dot product of a scalar gradient dotted with a material vector with
a scalar.
:Definition:
.. math::
\int_{\Omega} q \ul{y} \cdot \nabla p \mbox{ , }
\int_{\Omega} p \ul{y} \cdot \nabla q
:Arguments 1:
 material : :math:`\ul{y}`
 virtual : :math:`q`
 state : :math:`p`
:Arguments 2:
 material : :math:`\ul{y}`
 state : :math:`p`
 virtual : :math:`q`
"""
name = 'de_s_dot_mgrad_s'
arg_types = (('material', 'virtual', 'state'),
('material', 'state', 'virtual'))
arg_shapes = [{'material' : 'D, 1',
'virtual/grad_state' : (1, None),
'state/grad_state' : 1,
'virtual/grad_virtual' : (1, None),
'state/grad_virtual' : 1}]
modes = ('grad_state', 'grad_virtual')
def get_function(self, mat, var1, var2, mode=None, term_mode=None,
diff_var=None, **kwargs):
return self.make_function(
'i0,0,0.i', mat, var1, var2, diff_var=diff_var,
)
How To Make a Release

.. toctree::
:maxdepth: 2
release_tasks
Module Index

sfepy package
^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/config
src/sfepy/version
sfepy.applications package
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/applications/application
src/sfepy/applications/evp_solver_app
src/sfepy/applications/pde_solver_app
sfepy.base package
^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/base/base
src/sfepy/base/compat
src/sfepy/base/conf
src/sfepy/base/getch
src/sfepy/base/goptions
src/sfepy/base/ioutils
src/sfepy/base/log
src/sfepy/base/log_plotter
src/sfepy/base/mem_usage
src/sfepy/base/multiproc
src/sfepy/base/multiproc_mpi
src/sfepy/base/multiproc_proc
src/sfepy/base/parse_conf
src/sfepy/base/plotutils
src/sfepy/base/reader
src/sfepy/base/resolve_deps
src/sfepy/base/testing
src/sfepy/base/timing
sfepy.discrete package
^^^^^^^^^^^^^^^^^^^^^^
This package implements various PDE discretization schemes (FEM or IGA).
.. toctree::
:maxdepth: 2
src/sfepy/discrete/conditions
src/sfepy/discrete/equations
src/sfepy/discrete/evaluate
src/sfepy/discrete/evaluate_variable
src/sfepy/discrete/functions
src/sfepy/discrete/integrals
src/sfepy/discrete/materials
src/sfepy/discrete/parse_equations
src/sfepy/discrete/parse_regions
src/sfepy/discrete/probes
src/sfepy/discrete/problem
src/sfepy/discrete/projections
src/sfepy/discrete/quadratures
src/sfepy/discrete/simplex_cubature
src/sfepy/discrete/variables
sfepy.discrete.common subpackage
"""""""""""""""""""""""""""""""""
Common lowerlevel code and parent classes for FEM and IGA.
.. toctree::
:maxdepth: 2
src/sfepy/discrete/common/dof_info
src/sfepy/discrete/common/domain
src/sfepy/discrete/common/extmods/_fmfield
src/sfepy/discrete/common/extmods/_geommech
src/sfepy/discrete/common/extmods/assemble
src/sfepy/discrete/common/extmods/cmesh
src/sfepy/discrete/common/extmods/crefcoors
src/sfepy/discrete/common/extmods/mappings
src/sfepy/discrete/common/fields
src/sfepy/discrete/common/global_interp
src/sfepy/discrete/common/mappings
src/sfepy/discrete/common/poly_spaces
src/sfepy/discrete/common/region
sfepy.discrete.fem subpackage
""""""""""""""""""""""""""""""
.. toctree::
:maxdepth: 2
src/sfepy/discrete/fem/domain
src/sfepy/discrete/fem/extmods/bases
src/sfepy/discrete/fem/extmods/lobatto_bases
src/sfepy/discrete/fem/facets
src/sfepy/discrete/fem/fe_surface
src/sfepy/discrete/fem/fields_base
src/sfepy/discrete/fem/fields_hierarchic
src/sfepy/discrete/fem/fields_nodal
src/sfepy/discrete/fem/fields_positive
src/sfepy/discrete/fem/geometry_element
src/sfepy/discrete/fem/history
src/sfepy/discrete/fem/lcbc_operators
src/sfepy/discrete/fem/linearizer
src/sfepy/discrete/fem/mappings
src/sfepy/discrete/fem/mesh
src/sfepy/discrete/fem/meshio
src/sfepy/discrete/fem/periodic
src/sfepy/discrete/fem/poly_spaces
src/sfepy/discrete/fem/refine
src/sfepy/discrete/fem/refine_hanging
src/sfepy/discrete/fem/_serendipity
src/sfepy/discrete/fem/utils
sfepy.discrete.dg subpackage
"""""""""""""""""""""""""""""
.. toctree::
:maxdepth: 2
src/sfepy/discrete/dg/dg_1D_vizualizer
src/sfepy/discrete/dg/fields
src/sfepy/discrete/dg/poly_spaces
src/sfepy/discrete/dg/limiters
src/sfepy/solvers/ts_dg_solvers
sfepy.discrete.iga subpackage
""""""""""""""""""""""""""""""
.. toctree::
:maxdepth: 2
src/sfepy/discrete/iga/domain
src/sfepy/discrete/iga/domain_generators
src/sfepy/discrete/iga/extmods/igac
src/sfepy/discrete/iga/fields
src/sfepy/discrete/iga/iga
src/sfepy/discrete/iga/io
src/sfepy/discrete/iga/mappings
src/sfepy/discrete/iga/plot_nurbs
src/sfepy/discrete/iga/utils
sfepy.discrete.structural subpackage
"""""""""""""""""""""""""""""""""""""
.. toctree::
:maxdepth: 2
src/sfepy/discrete/structural/fields
src/sfepy/discrete/structural/mappings
sfepy.homogenization package
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/homogenization/band_gaps_app
src/sfepy/homogenization/coefficients
src/sfepy/homogenization/coefs_base
src/sfepy/homogenization/coefs_elastic
src/sfepy/homogenization/coefs_perfusion
src/sfepy/homogenization/coefs_phononic
src/sfepy/homogenization/convolutions
src/sfepy/homogenization/engine
src/sfepy/homogenization/homogen_app
src/sfepy/homogenization/micmac
src/sfepy/homogenization/recovery
src/sfepy/homogenization/utils
sfepy.linalg package
^^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/linalg/check_derivatives
src/sfepy/linalg/eigen
src/sfepy/linalg/geometry
src/sfepy/linalg/sparse
src/sfepy/linalg/sympy_operators
src/sfepy/linalg/utils
sfepy.mechanics package
^^^^^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/mechanics/contact_bodies
src/sfepy/mechanics/elastic_constants
src/sfepy/mechanics/matcoefs
src/sfepy/mechanics/membranes
src/sfepy/mechanics/shell10x
src/sfepy/mechanics/tensors
src/sfepy/mechanics/units
src/sfepy/mechanics/extmods/ccontres
sfepy.mesh package
^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/mesh/bspline
src/sfepy/mesh/geom_tools
src/sfepy/mesh/mesh_generators
src/sfepy/mesh/mesh_tools
src/sfepy/mesh/splinebox
.. _sfepy_parallel_package:
sfepy.parallel package
^^^^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/parallel/evaluate
src/sfepy/parallel/parallel
src/sfepy/parallel/plot_parallel_dofs
sfepy.postprocess package
^^^^^^^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/postprocess/plot_cmesh
src/sfepy/postprocess/plot_dofs
src/sfepy/postprocess/plot_facets
src/sfepy/postprocess/plot_quadrature
src/sfepy/postprocess/probes_vtk
src/sfepy/postprocess/time_history
src/sfepy/postprocess/utils_vtk
.. _sfepy_solvers:
sfepy.solvers package
^^^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/solvers/auto_fallback
src/sfepy/solvers/eigen
src/sfepy/solvers/ls
src/sfepy/solvers/ls_mumps
src/sfepy/solvers/ls_mumps_parallel
src/sfepy/solvers/nls
src/sfepy/solvers/optimize
src/sfepy/solvers/oseen
src/sfepy/solvers/qeigen
src/sfepy/solvers/semismooth_newton
src/sfepy/solvers/solvers
src/sfepy/solvers/ts
src/sfepy/solvers/ts_solvers
sfepy.terms package
^^^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
terms_overview
src/sfepy/terms/terms
src/sfepy/terms/terms_adj_navier_stokes
src/sfepy/terms/terms_basic
src/sfepy/terms/terms_biot
src/sfepy/terms/terms_compat
src/sfepy/terms/terms_constraints
src/sfepy/terms/terms_contact
src/sfepy/terms/terms_dg
src/sfepy/terms/terms_diffusion
src/sfepy/terms/terms_dot
src/sfepy/terms/terms_elastic
src/sfepy/terms/terms_electric
src/sfepy/terms/terms_fibres
src/sfepy/terms/terms_hyperelastic_base
src/sfepy/terms/terms_hyperelastic_tl
src/sfepy/terms/terms_hyperelastic_ul
src/sfepy/terms/terms_membrane
src/sfepy/terms/terms_multilinear
src/sfepy/terms/terms_navier_stokes
src/sfepy/terms/terms_piezo
src/sfepy/terms/terms_point
src/sfepy/terms/terms_sensitivity
src/sfepy/terms/terms_shells
src/sfepy/terms/terms_surface
src/sfepy/terms/terms_th
src/sfepy/terms/terms_volume
src/sfepy/terms/utils
src/sfepy/terms/extmods/terms
Scripts
^^^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/scripts/blockgen
src/sfepy/scripts/convert_mesh
src/sfepy/scripts/cylindergen
src/sfepy/scripts/gen_iga_patch
src/sfepy/scripts/gen_mesh
src/sfepy/scripts/gen_mesh_prev
src/sfepy/scripts/plot_condition_numbers
src/sfepy/scripts/plot_logs
src/sfepy/scripts/plot_mesh
src/sfepy/scripts/plot_quadratures
src/sfepy/scripts/plot_times
src/sfepy/scripts/probe
src/sfepy/scripts/resview
src/sfepy/scripts/simple
Tests
^^^^^
.. toctree::
:maxdepth: 2
src/sfepy/tests/conftest
src/sfepy/tests/test_assembling
src/sfepy/tests/test_base
src/sfepy/tests/test_cmesh
src/sfepy/tests/test_conditions
src/sfepy/tests/test_declarative_examples
src/sfepy/tests/test_dg_field
src/sfepy/tests/test_dg_terms_calls
src/sfepy/tests/test_domain
src/sfepy/tests/test_eigenvalue_solvers
src/sfepy/tests/test_elasticity_small_strain
src/sfepy/tests/test_fem
src/sfepy/tests/test_functions
src/sfepy/tests/test_high_level
src/sfepy/tests/test_homogenization_engine
src/sfepy/tests/test_homogenization_perfusion
src/sfepy/tests/test_hyperelastic_tlul
src/sfepy/tests/test_io
src/sfepy/tests/test_laplace_unit_disk
src/sfepy/tests/test_laplace_unit_square
src/sfepy/tests/test_lcbcs
src/sfepy/tests/test_linalg
src/sfepy/tests/test_linear_solvers
src/sfepy/tests/test_linearization
src/sfepy/tests/test_log
src/sfepy/tests/test_matcoefs
src/sfepy/tests/test_mesh_expand
src/sfepy/tests/test_mesh_generators
src/sfepy/tests/test_mesh_interp
src/sfepy/tests/test_mesh_smoothing
src/sfepy/tests/test_meshio
src/sfepy/tests/test_msm_laplace
src/sfepy/tests/test_msm_symbolic
src/sfepy/tests/test_normals
src/sfepy/tests/test_parsing
src/sfepy/tests/test_poly_spaces
src/sfepy/tests/test_projections
src/sfepy/tests/test_quadratures
src/sfepy/tests/test_ref_coors
src/sfepy/tests/test_refine_hanging
src/sfepy/tests/test_regions
src/sfepy/tests/test_semismooth_newton
src/sfepy/tests/test_sparse
src/sfepy/tests/test_splinebox
src/sfepy/tests/test_tensors
src/sfepy/tests/test_term_call_modes
src/sfepy/tests/test_term_consistency
src/sfepy/tests/test_term_sensitivity
src/sfepy/tests/test_units
src/sfepy/tests/test_volume
Tools
^^^^^
.. toctree::
:maxdepth: 2
src/tools/build_helpers
src/tools/gen_gallery
src/tools/gen_legendre_simplex_base
src/tools/gen_lobatto1d_c
src/tools/gen_release_notes
src/tools/gen_serendipity_basis
src/tools/gen_solver_table
src/tools/gen_term_table
src/tools/show_authors
src/tools/show_terms_use
src/tools/sync_module_docs