#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
#
#
# Python GetFEM++ interface
#
# Copyright (C) 2004-2009 Yves Renard, Julien Pommier.
#                                                       
# This file is a part of GETFEM++                                         
#                                                                         
# GetFEM++  is  free software;  you  can  redistribute  it  and/or modify it
# under  the  terms  of the  GNU  Lesser General Public License as published
# by  the  Free Software Foundation;  either version 2.1 of the License,  or
# (at your option) any later version.
# This program  is  distributed  in  the  hope  that it will be useful,  but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or  FITNESS  FOR  A PARTICULAR PURPOSE.  See the GNU Lesser General Public
# License for more details.
# You  should  have received a copy of the GNU Lesser General Public License
# along  with  this program;  if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
#                                                                         
"""Getfem-interface classes.
Provides access to the pseudo-objects exported by the python-getfem interface.
"""


__version__ = "$Revision: 3056 $"
# $Source: getfem++/interface/src/python/getfem.base.py,v $

import sys
from numpy import *

from _getfem import *
obj_count = {}
getfem('workspace','clear all')

def generic_constructor(self,clname,*args):
    """Internal function -- acts as a constructor for all getfem objects."""
#    print 'generic_constructor.'+clname+'('+str(args)+')'
    if (len(args)==1 and type(args[0]) is GetfemObject):
        self.id = args[0]
    else:
        self.id = getfem_from_constructor(clname,*args)
    obj_count[self.id] = obj_count.get(self.id,0)+1

def generic_destructor(self,destructible=True):
    """Internal function -- acts as a destructor for all getfem objects."""
    if (not hasattr(self,'id')):
        return
#    print "Mesh.__del__       ",self.id,'count=',obj_count[self.id]
    if (obj_count.has_key(self.id)):
        obj_count[self.id] = obj_count[self.id]-1
        if (destructible and obj_count[self.id] == 0):
#            print "effective deletion"
            getfem('delete',self.id)

# stub classes for getfem-interface objects

class Mesh:
    """Getfem Mesh Object.

Thos object is able to store any element in any dimension even 
if you mix elements with different dimensions.
    """
    def __init__(self, *args):
        """General constructor for Mesh objects.

* M = Mesh('empty', int dim)
  Create a new empty mesh.
* M = Mesh('cartesian', vec X[, vec Y[, vec Z,..]])
  Build quickly a regular mesh of quadrangles, cubes, etc.
* M = Mesh('triangles_grid', vec X, vec Y)
  Build quickly a regular mesh of triangles. This is a very limited and
  somehow deprecated function (See also ``Mesh('ptND')``,
  ``Mesh('regular_simplices')`` and ``Mesh('cartesian')``).
* M = Mesh('regular_simplices', vec X[, vec Y[, vec Z,...]]['degree', int \
k]['noised'])
  Mesh a n-dimensionnal parallelepipeded with simplices (triangles,
  tetrahedrons etc) . The optional degree may be used to build meshes with
  non linear geometric transformations.
* M = Mesh('curved', Mesh m0, vec F)
  Build a curved (n+1)-dimensions mesh from a n-dimensions mesh `m0`. The
  points of the new mesh have one additional coordinate, given by the vector
  `F`. This can be used to obtain meshes for shells. `m0` may be a MeshFem
  object, in that case its linked mesh will be used.
* M = Mesh('prismatic', Mesh m0, int NLAY)
  Extrude a prismatic Mesh `M` from a Mesh `m0`. In the additional dimension
  there are `NLAY` layers of elements built from ``0`` to ``1``.
* M = Mesh('pt2D', mat P, ivec T[, int n])
  Build a mesh from a 2D triangulation. Each column of `P` contains a point
  coordinate, and each column of `T` contains the point indices of a
  triangle. `n` is optional and is a zone number. If `n` is specified then
  only the zone number `n` is converted (in that case, `T` is expected to
  have 4 rows, the fourth containing these zone numbers).
* M = Mesh('ptND', mat P, imat T)
  Build a mesh from a N-dimensional "triangulation". Similar function to
  'pt2D', for building simplexes meshes from a triangulation given in `T`,
  and a list of points given in `P`. The dimension of the mesh will be the
  number of rows of `P`, and the dimension of the simplexes will be the
  number of rows of `T`.
* M = Mesh('load', string filename)
  Load a mesh from a GETFEM++ ascii mesh file. See also Mesh.save(filename).
* M = Mesh('from_string', string s)
  Load a mesh from a string description.

  For example, a string returned by ``Mesh.char()``.
* M = Mesh('import', string format, string filename)
  Import a mesh. `format` may be: - 'gmsh' for a mesh created with `gmsh
  <http://www.geuz.org/gmsh>`_
  - 'gid' for a mesh created with `GiD <http://gid.cimne.upc.es>`_
  - 'am_fmt' for a mesh created with `emc2
  <http://pauillac.inria.fr/cdrom/www/emc2/fra.htm>`_
* M = Mesh('clone', Mesh m2)
  Create a copy of a mesh.
        """
        generic_constructor(self,'mesh',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def get(self, *args):
        return getfem('mesh_get',self.id, *args)
    def set(self, *args):
        return getfem('mesh_set',self.id, *args)
    def __str__(self):
        return self.char()
    def __repr__(self):
        return '<getfem.Mesh %dD, %d points, %d convexes, %d bytes>' % \
               (self.dim(),self.nbpts(),self.nbcvs(),self.memsize())

    #@RDATTR MESH:GET('dim')
    def dim(self):
        """Get the dimension of the mesh (2 for a 2D mesh, etc)."""
        return self.get("dim")


    #@RDATTR MESH:GET('nbpts')
    def nbpts(self):
        """Get the number of points of the mesh."""
        return self.get("nbpts")


    #@RDATTR MESH:GET('nbcvs')
    def nbcvs(self):
        """Get the number of convexes of the mesh."""
        return self.get("nbcvs")


    #@GET    MESH:GET('pts')
    def pts(self, PIDs=None):
        """Return the list of point coordinates of the mesh.

Synopsis: P = Mesh.pts([, ivec PIDs])

Each column of the returned matrix contains the coordinates of one point. If
the optional argument `PIDs` was given, only the points whose #id is listed in
this vector are returned. Otherwise, the returned matrix will have
Mesh.max_pid() columns, which might be greater than Mesh.nbpts() (if some
points of the mesh have been destroyed and no call to
Mesh.optimize_structure() have been issued). The columns corresponding to
deleted points will be filled with NaN. You can use Mesh.pid() to filter such
invalid points."""
        return self.get("pts", PIDs)


    #@GET    MESH:GET('pid')
    def pid(self):
        """Return the list of points #id of the mesh.

Synopsis: Pid = Mesh.pid()

Note that their numbering is not supposed to be contiguous from 0 to
Mesh.nbpts()-1, especially if some points have been removed from the mesh. You
can use Mesh.optimize_structure() to enforce a contiguous numbering."""
        return self.get("pid")


    #@GET    MESH:GET('pid in faces')
    def pid_in_faces(self, CVFIDs):
        """Search point #id listed in `CVFIDs`.

Synopsis: PIDs = Mesh.pid_in_faces(imat CVFIDs)

`CVFIDs` is a two-rows matrix, the first row lists convex #ids, and the second
lists face numbers. On return, `PIDs` is a vector containing points #id."""
        return self.get("pid in faces", CVFIDs)


    #@GET    MESH:GET('pid in cvids')
    def pid_in_cvids(self, CVIDs):
        """Search point #id listed in `CVIDs`.

Synopsis: PIDs = Mesh.pid_in_cvids(imat CVIDs)

`PIDs` is a vector containing points #id."""
        return self.get("pid in cvids", CVIDs)


    #@GET    MESH:GET('pid in regions')
    def pid_in_regions(self, RIDs):
        """Search point #id listed in `RIDs`.

Synopsis: PIDs = Mesh.pid_in_regions(imat RIDs)

`PIDs` is a vector containing points #id."""
        return self.get("pid in regions", RIDs)


    #@GET    MESH:GET('pid from coords')
    def pid_from_coords(self, PTS, radius=0):
        """Search point #id whose coordinates are listed in `PTS`.

Synopsis: PIDs = Mesh.pid_from_coords(mat PTS[, scalar radius=0])

`PTS` is an array containing a list of point coordinates. On return, `PIDs` is
a vector containing points #id for each point found in `eps` range, and -1 for
those which where not found in the mesh."""
        return self.get("pid from coords", PTS, radius)


    #@GET    MESH:GET('pid from cvid')
    def pid_from_cvid(self, CVIDs=None):
        """Return the points attached to each convex of the mesh.

Synopsis: (Pid, IDx) = Mesh.pid_from_cvid([,imat CVIDs])

If `CVIDs` is omitted, all the convexes will be considered (equivalent to
`CVIDs = Mesh.max_cvid()`). `IDx` is a vector, length(IDx) = length(CVIDs)+1.
`Pid` is a vector containing the concatenated list of #id of points of each
convex in `CVIDs`. Each entry of `IDx` is the position of the corresponding
convex point list in `Pid`. Hence, for example, the list of #id of points of
the second convex is Pid[IDx(2):IDx(3)].

If `CVIDs` contains convex #id which do not exist in the mesh, their point
list will be empty."""
        return self.get("pid from cvid", CVIDs)


    #@GET    MESH:GET('cvid')
    def cvid(self):
        """Return the list of all convex #id.

Synopsis: CVid = Mesh.cvid()

Note that their numbering is not supposed to be contiguous from 0 to
Mesh.nbcvs()-1, especially if some points have been removed from the mesh. You
can use Mesh.optimize_structure() to enforce a contiguous numbering."""
        return self.get("cvid")


    #@GET    MESH:GET('max pid')
    def max_pid(self):
        """Return the maximum #id of all points in the mesh (see 'max cvid')."""
        return self.get("max pid")


    #@GET    MESH:GET('max cvid')
    def max_cvid(self):
        """Return the maximum #id of all convexes in the mesh (see 'max pid')."""
        return self.get("max cvid")


    #@GET    MESH:GET('edges')
    def edges(self, CVLST=None, *args):
        """[OBSOLETE FUNCTION! will be removed in a future release]

Synopsis: [E,C] = Mesh.edges([, CVLST][,'merge'])

Return the list of edges of mesh M for the convexes listed in the row vector
CVLST. E is a 2 x nb_edges matrix containing point indices. If CVLST is
omitted, then the edges of all convexes are returned. If CVLST has two rows
then the first row is supposed to contain convex numbers, and the second face
numbers, of which the edges will be returned. If 'merge' is indicated, all
common edges of convexes are merged in a single edge. If the optional output
argument C is specified, it will contain the convex number associated with
each edge."""
        return self.get("edges", CVLST, *args)


    #@GET    MESH:GET('curved edges')
    def curved_edges(self, N, CVLST=None):
        """[OBSOLETE FUNCTION! will be removed in a future release]

Synopsis: [E,C] = Mesh.curved_edges(int N [, CVLST])

More sophisticated version of Mesh.edges() designed for curved elements. This
one will return N (N>=2) points of the (curved) edges. With N==2, this is
equivalent to Mesh.edges(). Since the points are no more always part of the
mesh, their coordinates are returned instead of points number, in the array E
which is a [ mesh_dim x 2 x nb_edges ] array. If the optional output argument
C is specified, it will contain the convex number associated with each edge."""
        return self.get("curved edges", N, CVLST)


    #@GET    MESH:GET('orphaned pid')
    def orphaned_pid(self):
        """Search point #id which are not linked to a convex."""
        return self.get("orphaned pid")


    #@GET    MESH:GET('cvid from pid')
    def cvid_from_pid(self, PIDs, share=False):
        """Search convex #ids related with the point #ids given in `PIDs`.

Synopsis: CVIDs = Mesh.cvid_from_pid(ivec PIDs[, bool share=False])

If `share=False`, search convex whose vertex #ids are in `PIDs`. If
`share=True`, search convex #ids that share the point #ids given in `PIDs`.
`CVIDs` is a vector (possibly empty)."""
        return self.get("cvid from pid", PIDs, share)


    #@GET    MESH:GET('faces from pid')
    def faces_from_pid(self, PIDs):
        """Return the convex faces whose vertex #ids are in `PIDs`.

Synopsis: CVFIDs = Mesh.faces_from_pid(ivec PIDs)

For a convex face to be returned, EACH of its points have to be listed in
`PIDs`. On output, the first row of `CVf` contains the convex number, and the
second row contains the face number (local number in the convex)."""
        return self.get("faces from pid", PIDs)


    #@GET    MESH:GET('outer faces')
    def outer_faces(self, CVIDs=None):
        """Return the faces which are not shared by two convexes.

Synopsis: CVFIDs = Mesh.outer_faces([, CVIDs])

If `CVIDs` is not given, it basically returns the mesh boundary. If `CVIDs` is
given, it returns the boundary of the convex set whose #ids are listed in
`CVIDs`."""
        return self.get("outer faces", CVIDs)


    #@GET    MESH:GET('faces from cvid')
    def faces_from_cvid(self, CVIDs=None, *args):
        """Return a list of convexes faces from a list of convex #id.

Synopsis: CVFIDs = Mesh.faces_from_cvid([, ivec CVIDs][, 'merge'])

`CVFIDs` is a two-rows matrix, the first row lists convex #ids, and the second
lists face numbers. If `CVIDs` is not given, all convexes are considered. The
optional argument 'merge' merges faces shared by the convex of `CVIDs`."""
        return self.get("faces from cvid", CVIDs, *args)


    #@GET    MESH:GET('triangulated surface')
    def triangulated_surface(self, Nrefine, CVLIST=None):
        """[OBSOLETE FUNCTION! will be removed in a future release]

Synopsis: [mat T] = Mesh.triangulated_surface(int Nrefine [,CVLIST])

Similar function to Mesh.curved_edges() : split (if necessary, i.e. if the
geometric transformation if non-linear) each face into sub-triangles and
return their coordinates in T (see also compute_eval on P1 tri mesh(mf, U, ))"""
        return self.get("triangulated surface", Nrefine, CVLIST)


    #@GET    MESH:GET('normal of face')
    def normal_of_face(self, cv, f, nfpt=None):
        """Evaluates the normal of convex `cv`, face `f` at the `nfpt` point of the face.

Synopsis: N = Mesh.normal_of_face(int cv, int f[, int nfpt])

If `nfpt` is not specified, then the normal is evaluated at each geometrical
node of the face."""
        return self.get("normal of face", cv, f, nfpt)


    #@GET    MESH:GET('normal of faces')
    def normal_of_faces(self, CVFIDs):
        """Evaluates (at face centers) the normals of convexes.

Synopsis: N = Mesh.normal_of_faces(imat CVFIDs)

`CVFIDs` is supposed to contain convex numbers in its first row and convex
face number in its second row."""
        return self.get("normal of faces", CVFIDs)


    #@GET    MESH:GET('quality')
    def quality(self, CVIDs=None):
        """Return an estimation of the quality of each convex (0 <= Q <= 1)."""
        return self.get("quality", CVIDs)


    #@GET    MESH:GET('convex area')
    def convex_area(self, CVIDs=None):
        """Return an estimation of the area of each convex."""
        return self.get("convex area", CVIDs)


    #@GET    MESH:GET('cvstruct')
    def cvstruct(self, CVIDs=None):
        """Return an array of the convex structures.

Synopsis: (S, CV2S) = Mesh.cvstruct([, ivec CVIDs])

If `CVIDs` is not given, all convexes are considered. Each convex structure is
listed once in `S`, and `CV2S` maps the convexes indice in `CVIDs` to the
indice of its structure in `S`."""
        return self.get("cvstruct", CVIDs)


    #@GET    MESH:GET('geotrans')
    def geotrans(self, CVIDs=None):
        """Returns an array of the geometric transformations.

Synopsis: (GT, CV2GT) = Mesh.geotrans([, ivec CVIDs])

See also Mesh.cvstruct()."""
        return self.get("geotrans", CVIDs)


    #@GET    MESH:GET('regions')
    def regions(self):
        """Return the list of valid regions stored in the mesh."""
        return self.get("regions")


    #@GET    MESH:GET('region')
    def region(self, RIDs):
        """Return the list of convexes/faces on the regions `RIDs`.

Synopsis: CVFIDs = Mesh.region(ivec RIDs)

On output, the first row of `CVFIDs` contains the convex numbers, and the
second row contains the face numbers (and -1 when the whole convex is in the
regions)."""
        return self.get("region", RIDs)


    #@GET    MESH:GET('save')
    def save(self, filename):
        """Save the mesh object to an ascii file.

Synopsis: Mesh.save(string filename)

This mesh can be restored with Mesh('load', filename)."""
        return self.get("save", filename)


    #@GET    MESH:GET('char')
    def char(self):
        """Output a string description of the mesh."""
        return self.get("char")


    #@GET    MESH:GET('export to vtk')
    def export_to_vtk(self, filename, *args):
        """Exports a mesh to a VTK file .

Synopsis: Mesh.export_to_vtk(string filename, ... [,'ascii'][,'quality'])

If 'quality' is specified, an estimation of the quality of each convex will be
written to the file. See also MeshFem.export_to_vtk(), Slice.export_to_vtk()."""
        return self.get("export to vtk", filename, *args)


    #@GET    MESH:GET('export to dx')
    def export_to_dx(self, filename, *args):
        """Exports a mesh to an OpenDX file.

Synopsis: Mesh.export_to_dx(string filename, ...
[,'ascii'][,'append'][,'as',string name,[,'serie',string
serie_name]][,'edges'])

See also MeshFem.export_to_dx(), Slice.export_to_dx()."""
        return self.get("export to dx", filename, *args)


    #@GET    MESH:GET('export to pos')
    def export_to_pos(self, filename):
        """Exports a mesh to a POS file .

Synopsis: Mesh.export_to_pos(string filename)

See also MeshFem.export_to_pos(), Slice.export_to_pos()."""
        return self.get("export to pos", filename)


    #@GET    MESH:GET('memsize')
    def memsize(self):
        """Return the amount of memory (in bytes) used by the mesh."""
        return self.get("memsize")



    #@SET    MESH:SET('pts')
    def set_pts(self, PTS):
        """Replace the coordinates of the mesh points with those given in `PTS`."""
        return self.set("pts", PTS)


    #@SET    MESH:SET('add point')
    def add_point(self, PTS):
        """Insert new points in the mesh and return their #ids.

Synopsis: PIDs = Mesh.add_point(mat PTS)

`PTS` should be an ``nxm`` matrix , where ``n`` is the mesh dimension, and
``m`` is the number of points that will be added to the mesh. On output,
`PIDs` contains the point #ids of these new points.

Remark: if some points are already part of the mesh (with a small tolerance of
approximately ``1e-8``), they won't be inserted again, and `PIDs` will contain
the previously assigned #ids of these points."""
        return self.set("add point", PTS)


    #@SET    MESH:SET('del point')
    def del_point(self, PIDs):
        """Removes one or more points from the mesh.

Synopsis: Mesh.del_point(ivec PIDs)

`PIDs` should contain the point #ids, such as the one returned by the 'add
point' command."""
        return self.set("del point", PIDs)


    #@SET    MESH:SET('add convex')
    def add_convex(self, GT, PTS):
        """Add a new convex into the mesh.

Synopsis: CVIDs = Mesh.add_convex(GeoTrans GT, mat PTS)

The convex structure (triangle, prism,...) is given by `GT` (obtained with
GeoTrans('...')), and its points are given by the columns of `PTS`. On return,
`CVIDs` contains the convex #ids. `PTS` might be a 3-dimensional array in
order to insert more than one convex (or a two dimensional array correctly
shaped according to Fortran ordering)."""
        return self.set("add convex", GT, PTS)


    #@SET    MESH:SET('del convex')
    def del_convex(self, CVIDs):
        """Remove one or more convexes from the mesh.

Synopsis: Mesh.del_convex(mat CVIDs)

`CVIDs` should contain the convexes #ids, such as the ones returned by the
'add convex' command."""
        return self.set("del convex", CVIDs)


    #@SET    MESH:SET('del convex of dim')
    def del_convex_of_dim(self, DIMs):
        """Remove all convexes of dimension listed in `DIMs`.

Synopsis: Mesh.del_convex_of_dim(ivec DIMs)

For example; ``Mesh.del_convex_of_dim( [1,2])`` remove all line segments,
triangles and quadrangles."""
        return self.set("del convex of dim", DIMs)


    #@SET    MESH:SET('translate')
    def translate(self, V):
        """Translates each point of the mesh from `V`."""
        return self.set("translate", V)


    #@SET    MESH:SET('transform')
    def transform(self, T):
        """Applies the matrix `T` to each point of the mesh.

Synopsis: Mesh.transform(mat T)

Note that `T` is not required to be a ``NxN`` matrix (with ``N =
Mesh.dim()``). Hence it is possible to transform a 2D mesh into a 3D one (and
reciprocally)."""
        return self.set("transform", T)


    #@SET    MESH:SET('region')
    def set_region(self, rnum, CVFIDs):
        """Assigns the region number `rnum` to the convex faces stored in each column of
the matrix `CVFIDs`. The first row of `CVFIDs` contains a convex #ids, and
the second row contains a face number in the convex (or ``-1`` for the whole
convex (regions are usually used to store a list of convex faces, but you may
also use them to store a list of convexes)."""
        return self.set("region", rnum, CVFIDs)


    #@SET    MESH:SET('region intersect')
    def region_intersect(self, r1, r2):
        """Replace the region number `r1` with its intersection with region number `r2`."""
        return self.set("region intersect", r1, r2)


    #@SET    MESH:SET('region merge')
    def region_merge(self, r1, r2):
        """Merge region number `r2` into region number `r1`."""
        return self.set("region merge", r1, r2)


    #@SET    MESH:SET('region substract')
    def region_substract(self, r1, r2):
        """Replace the region number `r1` with its difference with region number `r2`."""
        return self.set("region substract", r1, r2)


    #@SET    MESH:SET('delete region')
    def delete_region(self, RIDs):
        """Remove the regions whose #ids are listed in `RIDs`"""
        return self.set("delete region", RIDs)


    #@SET    MESH:SET('merge')
    def merge(self, m2):
        """Merge with the Mesh `m2`.

Synopsis: Mesh.merge(Mesh m2)

Overlapping points won't be duplicated. If `m2` is a MeshFem object, its
linked mesh will be used."""
        return self.set("merge", m2)


    #@SET    MESH:SET('optimize structure')
    def optimize_structure(self):
        """Reset point and convex numbering.

Synopsis: Mesh.optimize_structure()

After optimisation, the points (resp. convexes) will be consecutively numbered
from ``0`` to ``Mesh.max_pid()-1`` (resp. ``Mesh.max_cvid()-1``)."""
        return self.set("optimize structure")


    #@SET    MESH:SET('refine')
    def refine(self, CVIDs=None):
        """Use a Bank strategy for mesh refinement.

Synopsis: Mesh.refine([, ivec CVIDs])

If `CVIDs` is not given, the whole mesh is refined. Note that the regions, and
the finite element methods and integration methods of the MeshFem and MeshIm
objects linked to this mesh will be automagically refined."""
        return self.set("refine", CVIDs)



class MeshFem:
    def __init__(self, *args):
        """General constructor for MeshFem objects.

* MeshFem('load', string fname[, Mesh m])
  Load a MeshFem from a file. If the mesh `m` is not supplied (this kind of
  file does not store the mesh), then it is read from the file `fname` and
  its descriptor is returned as the second output argument.
* MeshFem('from_string', string [, mesh M])
  Create a MeshFem object from its string description. See also
  MeshFem.char()
* MeshFem('clone', MeshFem mf2)
  Create a copy of a MeshFem.
* MeshFem('sum', MeshFem mf1, MeshFem mf2[, MeshFem mf3[, ...]])
  Create a MeshFem that combines two (or more) MeshFem's. All MeshFem must
  share the same mesh (see Fem('interpolated_fem') to map a MeshFem onto
  another). After that, you should not modify the FEM of `mf1`, `mf2` etc.
* MeshFem('levelset', MeshFem mf, LevelSet ls)
  Create a MeshFem that is conformal to implicit surfaces defined by
  LevelSet's.
* MeshFem('partial', MeshFem mf, ivec DOFs[,ivec RCVs])
  Build a restricted MeshFem by keeping only a subset of the degrees of
  freedom of `mf`. If `RCVs` is given, no FEM will be put on the convexes
  listed in `RCVs`.
* MeshFem( Mesh m[, int Qdim=1])
  Build a new MeshFem object. The `Qdim` parameter is optional.
        """
        generic_constructor(self,'mesh_fem',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def get(self, *args):
        return getfem('mesh_fem_get',self.id, *args)
    def set(self, *args):
        return getfem('mesh_fem_set',self.id, *args)
    def __str__(self):
        return self.char()
    def __repr__(self):
        return '<getfem.MeshFem Q=%d, %d dof, %d (+%d) bytes>' % \
               (self.qdim(),self.nbdof(),self.memsize(), \
                self.linked_mesh().memsize())

    #@RDATTR MESHFEM:GET('nbdof')
    def nbdof(self):
        """Return the number of degrees of freedom (dof) of the MeshFem."""
        return self.get("nbdof")


    #@RDATTR MESHFEM:GET('nb basic dof')
    def nb_basic_dof(self):
        """Return the number of basic degrees of freedom (dof) of the MeshFem."""
        return self.get("nb basic dof")


    #@GET    MESHFEM:GET('basic dof from cv')
    def basic_dof_from_cv(self, CVids):
        """Return the dof of the convexes listed in `CVids`.

Synopsis: DOF = MeshFem.basic_dof_from_cv(mat CVids)

WARNING: the Degree of Freedom might be returned in ANY order, do not use this
function in your assembly routines. Use 'basic dof from cvid' instead, if you
want to be able to map a convex number with its associated degrees of freedom.
One can also get the list of basic dof on a set on convex faces, by indicating
on the second row of `CVids` the faces numbers (with respect to the convex
number on the first row)."""
        return self.get("basic dof from cv", CVids)


    #@GET    MESHFEM:GET('dof from cv')
    def dof_from_cv(self, CVids):
        """Deprecated function. Use MeshFem.basic_dof_from_cv() instead."""
        return self.get("dof from cv", CVids)


    #@GET    MESHFEM:GET('basic dof from cvid')
    def basic_dof_from_cvid(self, CVids=None):
        """Return the degrees of freedom attached to each convex of the mesh.

Synopsis: (DOFs, IDx) = MeshFem.basic_dof_from_cvid([, mat CVids])

If `CVids` is omitted, all the convexes will be considered (equivalent to
`CVids = 1 ... Mesh.max_cvid()`). `IDx` is a vector, `length(IDx) =
length(CVids)+1`. `DOFs` is a vector containing the concatenated list of dof
of each convex in `CVids`. Each entry of `IDx` is the position of the
corresponding convex point list in `DOFs`. Hence, for example, the list of
points of the second convex is DOFs[IDx(2):IDx(3)]. If `CVids` contains
convex #id which do not exist in the mesh, their point list will be empty."""
        return self.get("basic dof from cvid", CVids)


    #@GET    MESHFEM:GET('dof from cvid')
    def dof_from_cvid(self, CVids=None):
        """Deprecated function. Use MeshFem.basic_dof_from_cvid() instead."""
        return self.get("dof from cvid", CVids)


    #@GET    MESHFEM:GET('non conformal basic dof')
    def non_conformal_basic_dof(self, CVids=None):
        """Return partially linked degrees of freedom.

Synopsis: MeshFem.non_conformal_basic_dof([, mat CVids])

Return the basic dof located on the border of a convex and which belong to
only one convex, except the ones which are located on the border of the mesh.
For example, if the convex 'a' and 'b' share a common face, 'a' has a P1 FEM,
and 'b' has a P2 FEM, then the basic dof on the middle of the face will be
returned by this function (this can be useful when searching the interfaces
between classical FEM and hierarchical FEM)."""
        return self.get("non conformal basic dof", CVids)


    #@GET    MESHFEM:GET('non conformal dof')
    def non_conformal_dof(self, CVids=None):
        """Deprecated function. Use MeshFem.non_conformal_basic_dof() instead."""
        return self.get("non conformal dof", CVids)


    #@RDATTR MESHFEM:GET('qdim')
    def qdim(self):
        """Return the dimension Q of the field interpolated by the MeshFem.

Synopsis: MeshFem.qdim()

By default, Q=1 (scalar field). This has an impact on the dof numbering."""
        return self.get("qdim")


    #@GET    MESHFEM:GET('fem')
    def fem(self, CVids=None):
        """Return a list of FEM used by the MeshFem.

Synopsis: (FEMs, CV2F) = MeshFem.fem([, mat CVids])

`FEMs` is an array of all Fem objects found in the convexes given in `CVids`.
If `CV2F` was supplied as an output argument, it contains, for each convex
listed in `CVids`, the index of its correspounding FEM in `FEMs`. Convexes
which are not part of the mesh, or convexes which do not have any FEM have
their correspounding entry in `CV2F` set to -1."""
        return self.get("fem", CVids)


    #@GET    MESHFEM:GET('convex_index')
    def convex_index(self):
        """Return the list of convexes who have a FEM."""
        return self.get("convex_index")


    #@GET    MESHFEM:GET('is_lagrangian')
    def is_lagrangian(self, CVids=None):
        """Test if the MeshFem is Lagrangian.

Synopsis: bB = MeshFem.is_lagrangian([, mat CVids])

Lagrangian means that each base function Phi[i] is such that Phi[i](P[j]) =
delta(i,j), where P[j] is the dof location of the jth base function, and
delta(i,j) = 1 if i==j, else 0.

If `CVids` is omitted, it returns 1 if all convexes in the mesh are
Lagrangian. If `CVids` is used, it returns the convex indices (with respect to
`CVids`) which are Lagrangian."""
        return self.get("is_lagrangian", CVids)


    #@GET    MESHFEM:GET('is_equivalent')
    def is_equivalent(self, CVids=None):
        """Test if the MeshFem is equivalent.

Synopsis: bB = MeshFem.is_equivalent([, mat CVids])

See MeshFem.is_lagrangian()"""
        return self.get("is_equivalent", CVids)


    #@GET    MESHFEM:GET('is_polynomial')
    def is_polynomial(self, CVids=None):
        """Test if all base functions are polynomials.

Synopsis: bB = MeshFem.is_polynomial([, mat CVids])

See MeshFem.is_lagrangian()"""
        return self.get("is_polynomial", CVids)


    #@RDATTR MESHFEM:GET('is_reduced')
    def is_reduced(self):
        """Return 1 if the optional reduction matrix is applied to the dofs."""
        return self.get("is_reduced")


    #@GET    MESHFEM:GET('reduction matrix')
    def reduction_matrix(self):
        """Return the optional reduction matrix."""
        return self.get("reduction matrix")


    #@GET    MESHFEM:GET('extension matrix')
    def extension_matrix(self):
        """Return the optional extension matrix."""
        return self.get("extension matrix")


    #@GET    MESHFEM:GET('dof on region')
    def dof_on_region(self, Rs):
        """Return the list of dof (after the optional reduction) lying on one of the mesh
regions listed in `Rs`. More precisely, this function returns the basic dof
whose support is non-null on one of regions whose #ids are listed in `Rs`
(note that for boundary regions, some dof nodes may not lie exactly on the
boundary, for example the dof of Pk(n,0) lies on the center of the convex, but
the base function in not null on the convex border). For a reduced mesh_fem
a dof is lying on a region if its potential corresponding shape function is
nonzero on this region. The extension matrix is used to make the
correspondance between basic and reduced dofs."""
        return self.get("dof on region", Rs)


    #@GET    MESHFEM:GET('basic dof on region')
    def basic_dof_on_region(self, Rs):
        """Return the list of basic dof (before the optional reduction) lying on one of
the mesh regions listed in `Rs`. More precisely, this function returns the
basic dof whose support is non-null on one of regions whose #ids are listed in
`Rs` (note that for boundary regions, some dof nodes may not lie exactly on
the boundary, for example the dof of Pk(n,0) lies on the center of the convex,
but the base function in not null on the convex border)."""
        return self.get("basic dof on region", Rs)


    #@GET    MESHFEM:GET('basic dof nodes')
    def basic_dof_nodes(self, DOFids=None):
        """Get location of basic degrees of freedom.

Synopsis: DOFpts = MeshFem.basic_dof_nodes([, mat DOFids])

Return the list of interpolation points for the specified dof #IDs in `DOFids`
(if `DOFids` is omitted, all basic dof are considered)."""
        return self.get("basic dof nodes", DOFids)


    #@GET    MESHFEM:GET('dof nodes')
    def dof_nodes(self, DOFids=None):
        """Deprecated function. Use MeshFem.basic_dof_nodes() instead."""
        return self.get("dof nodes", DOFids)


    #@GET    MESHFEM:GET('dof partition')
    def dof_partition(self):
        """Get the 'dof_partition' array.

Synopsis: DOFP = MeshFem.dof_partition()

Return the array which associates an integer (the partition number) to each
convex of the MeshFem. By default, it is an all-zero array. The degrees of
freedom of each convex of the MeshFem are connected only to the dof of
neighbouring convexes which have the same partition number, hence it is
possible to create partially discontinuous MeshFem very easily."""
        return self.get("dof partition")


    #@GET    MESHFEM:GET('save')
    def save(self, filename, opt=None):
        """Save a MeshFem in a text file (and optionaly its linked mesh object if `opt`
is the string 'with_mesh')."""
        return self.get("save", filename, opt)


    #@GET    MESHFEM:GET('char')
    def char(self, opt=None):
        """Output a string description of the MeshFem.

Synopsis: MeshFem.char([, string opt])

By default, it does not include the description of the linked mesh object,
except if `opt` is 'with_mesh'."""
        return self.get("char", opt)


    #@GET    MESHFEM:GET('linked mesh')
    def linked_mesh(self):
        """Return a reference to the Mesh object linked to `mf`."""
        return self.get("linked mesh")


    #@GET    MESHFEM:GET('export to vtk')
    def export_to_vtk(self, filename, *args):
        """Export a MeshFem and some fields to a vtk file.

Synopsis: MeshFem.export_to_vtk(string filename, ... ['ascii'], U, 'name'...)

The FEM and geometric transformations will be mapped to order 1 or 2
isoparametric Pk (or Qk) FEMs (as VTK does not handle higher order elements).
If you need to represent high-order FEMs or high-order geometric
transformations, you should consider Slice.export_to_vtk()."""
        return self.get("export to vtk", filename, *args)


    #@GET    MESHFEM:GET('export to dx')
    def export_to_dx(self, filename, *args):
        """Export a MeshFem and some fields to an OpenDX file.

Synopsis: MeshFem.export_to_dx(string filename, ...['as', string
mesh_name][,'edges']['serie',string serie_name][,'ascii'][,'append'], U,
'name'...)

This function will fail if the MeshFem mixes different convex types (i.e.
quads and triangles), or if OpenDX does not handle a specific element type
(i.e. prism connections are not known by OpenDX). The FEM will be mapped to
order 1 Pk (or Qk) FEMs. If you need to represent high-order FEMs or high-
order geometric transformations, you should consider Slice.export_to_dx()."""
        return self.get("export to dx", filename, *args)


    #@GET    MESHFEM:GET('export to pos')
    def export_to_pos(self, filename, U1=None, nameU1=None, *args):
        """Export a MeshFem and some fields to a pos file.

Synopsis: MeshFem.export_to_pos(string filename[, mat U1, string nameU1[, mat
U2, string nameU2,...])

The FEM and geometric transformations will be mapped to order 1 isoparametric
Pk (or Qk) FEMs (as GMSH does not handle higher order elements)."""
        return self.get("export to pos", filename, U1, nameU1, *args)


    #@GET    MESHFEM:GET('dof_from_im')
    def dof_from_im(self, mim, p=None):
        """Return a selection of dof who contribute significantly to the mass-matrix that
would be computed with `mf` and the integration method `mim`. `p` represents
the dimension on what the integration method operates (default `p = mesh
dimension`). IMPORTANT: you still have to set a valid integration method on
the convexes which are not crosses by the levelset!"""
        return self.get("dof_from_im", mim, p)


    #@GET    MESHFEM:GET('interpolate_convex_data')
    def interpolate_convex_data(self, Ucv):
        """Interpolate data given on each convex of the mesh to the MeshFem dof. The
MeshFem has to be lagrangian, and should be discontinuous (typically a
FEM_PK(N,0) or FEM_QK(N,0) should be used). The last dimension of the input
vector Ucv should have Mesh.max_cvid() elements. Example of use:
MeshFem.interpolate_convex_data( Mesh.quality())"""
        return self.get("interpolate_convex_data", Ucv)


    #@GET    MESHFEM:GET('memsize')
    def memsize(self):
        """Return the amount of memory (in bytes) used by the mesh_fem object.

Synopsis: z = MeshFem.memsize()

The result does not take into account the linked mesh object."""
        return self.get("memsize")


    #@GET    MESHFEM:GET('has_linked_mesh_levelset')
    def has_linked_mesh_levelset(self):
        """???."""
        return self.get("has_linked_mesh_levelset")


    #@GET    MESHFEM:GET('linked_mesh_levelset')
    def linked_mesh_levelset(self):
        """???"""
        return self.get("linked_mesh_levelset")



    #@SET    MESHFEM:SET('fem')
    def set_fem(self, f, CVids=None):
        """Set the Finite Element Method.

Synopsis: MeshFem.set_fem(Fem f[, ivec CVids])

Assign a FEM `f` to all convexes whose #ids are listed in `CVids`. If `CVids`
is not given, the integration is assigned to all convexes. See the help of
Fem to obtain a list of available FEM methods."""
        return self.set("fem", f, CVids)


    #@SET    MESHFEM:SET('classical fem')
    def set_classical_fem(self, k, CVids=None):
        """Assign a classical (Lagrange polynomial) fem of order `k` to the MeshFem.

Synopsis: MeshFem.set_classical_fem(int k[, ivec CVids])

Uses FEM_PK for simplexes, FEM_QK for parallelepipeds etc."""
        return self.set("classical fem", k, CVids)


    #@SET    MESHFEM:SET('classical discontinuous fem')
    def set_classical_discontinuous_fem(self, K, IM_DEGREE=None, *args):
        """Assigns a classical (Lagrange polynomial) discontinuous fem or order K.

Synopsis: MeshFem.set_classical_discontinuous_fem(int K, [int IM_DEGREE [,ivec
CVIDX]])

Similar to MeshFem.set_classical_fem() except that FEM_PK_DISCONTINUOUS is
used."""
        return self.set("classical discontinuous fem", K, IM_DEGREE, *args)


    #@SET    MESHFEM:SET('qdim')
    def set_qdim(self, Q):
        """Change the `Q` dimension of the field that is interpolated by the MeshFem.

Synopsis: MeshFem.set_qdim(int Q)

`Q = 1` means that the MeshFem describes a scalar field, `Q = N` means that
the MeshFem describes a vector field of dimension N."""
        return self.set("qdim", Q)


    #@SET    MESHFEM:SET('reduction')
    def reduction(self, s):
        """Set or unset the use of the reduction/extension matrices."""
        return self.set("reduction", s)


    #@SET    MESHFEM:SET('reduction matrices')
    def reduction_matrices(self, R, E):
        """Set the reduction and extension matrices and valid their use."""
        return self.set("reduction matrices", R, E)


    #@SET    MESHFEM:SET('dof partition')
    def dof_partition(self, DOFP):
        """Change the 'dof_partition' array.

Synopsis: MeshFem.dof_partition(ivec DOFP)

`DOFP` is a vector holding a integer value for each convex of the MeshFem. See
MeshFem.dof_partition() for a description of "dof partition"."""
        return self.set("dof partition", DOFP)

    def eval(self, expression):
        """interpolate an expression on the (lagrangian) MeshFem.

Examples:

mf.eval('x[0]*x[1]') interpolates the function 'x*y'
mf.eval('[x[0],x[1]]') interpolates the vector field '[x,y]'
        """
        P = self.basic_dof_nodes()
        nbd = P.shape[1]

        if not self.is_lagrangian:
            raise RuntimeError('cannot eval on a non-Lagragian MeshFem')
        if self.qdim() != 1:
            Ind = arange(0,nbd,self.qdim()) # = sdof
            P   = P[:,Ind]
            nbd = P.shape[1] # = nb_sdof
        x = P[:,0]
        r = array(eval(expression))
        e = eval(expression)
        if (type(e) == type([3,3])):
            e=e.pop()
        Z = zeros(r.shape + (nbd,), type(e))
        # Z = zeros(r.shape + (nbd,), 'd');
        for i in range(0,nbd):
            x = P[:,i]
            Z[...,i] = eval(expression)
        return Z


class MeshIm:
    def __init__(self, *args):
        """General constructor for MeshIm objects.

* MeshIm('load', string fname[, Mesh m])
  Load a MeshIm from a file. If the mesh `m` is not supplied (this kind of
  file does not store the mesh), then it is read from the file and its
  descriptor is returned as the second output argument.
* MeshIm('from_string', string s[, mesh M])
  Create a MeshIm object from its string description. See also MeshIm.char()
* MeshIm('clone', MeshIm mim2)
  Create a copy of a MeshIm.
* MeshIm('levelset', LevelSet ls, string where, Integ im[, Integ im_tip])
  Build an integration method conformal to a partition defined implicitely by
  a levelset. The `where` argument define the domain of integration with
  respect to the levelset, it has to be chosen among 'ALL', 'INSIDE',
  'OUTSIDE' and 'BOUNDARY'.
* MeshIm(Mesh m, [{Integ im|int im_degree}])
  Build a new MeshIm object. For convenience, optional arguments (`im` or
  `im_degree`) can be provided, in that case a call to MeshIm.integ() is
  issued with these arguments.
        """
        generic_constructor(self,'mesh_im',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def get(self, *args):
        return getfem('mesh_im_get',self.id, *args)
    def set(self, *args):
        return getfem('mesh_im_set',self.id, *args)
    def __str__(self):
        return self.char()
    def __repr__(self):
        return '<getfem.MeshIm %d (+%d) bytes>' % \
               (self.memsize(), \
                self.linked_mesh().memsize())

    #@GET MESHIM:GET('integ')
    def integ(self, CVids=None):
        """Return a list of integration methods used by the MeshIm.

Synopsis: (I, CV2I) = MeshIm.integ([, mat CVids])

`I` is an array of all Integ objects found in the convexes given in `CVids`.
If `CV2I` was supplied as an output argument, it contains, for each convex
listed in `CVids`, the index of its correspounding integration method in `I`.
Convexes which are not part of the mesh, or convexes which do not have any
integration method have their correspounding entry in `CV2I` set to -1."""
        return self.get("integ", CVids)


    #@GET MESHFEM:GET('convex_index')
    def convex_index(self):
        """Return the list of convexes who have a FEM."""
        return self.get("convex_index")


    #@GET MESHIM:GET('eltm')
    def eltm(self, em, cv, f=None):
        """Return the elementary matrix (or tensor) integrated on the convex `cv`.

Synopsis: M = MeshIm.eltm(Eltm em, int cv [int f])

**WARNING** Be sure that the fem used for the construction of `em` is
compatible with the fem assigned to element `cv` ! This is not checked by the
function ! If the argument `f` is given, then the elementary tensor is
integrated on the face `f` of `cv` instead of the whole convex."""
        return self.get("eltm", em, cv, f)


    #@GET MESHIM:GET('im_nodes')
    def im_nodes(self, CVids=None):
        """Return the coordinates of the integration points, with their weights.

Synopsis: Ip = MeshIm.im_nodes([, mat CVids])

`CVids` may be a list of convexes, or a list of convex faces, such as returned
by Mesh.region()"""
        return self.get("im_nodes", CVids)


    #@GET MESHIM:GET('save')
    def save(self, filename):
        """Saves a MeshIm in a text file (and optionaly its linked mesh object)."""
        return self.get("save", filename)


    #@GET MESHIM:GET('char')
    def char(self):
        """Output a string description of the MeshIm.

Synopsis: MeshIm.char([,'with mesh'])

By default, it does not include the description of the linked Mesh object."""
        return self.get("char")


    #@GET MESHIM:GET('linked mesh')
    def linked_mesh(self):
        """Returns a reference to the Mesh object linked to `mim`."""
        return self.get("linked mesh")


    #@GET MESHIM:GET('memsize')
    def memsize(self):
        """Return the amount of memory (in bytes) used by the MeshIm object.

Synopsis: z = MeshIm.memsize()

The result does not take into account the linked Mesh object."""
        return self.get("memsize")



    #@SET MESHIM:SET('integ')
    def set_integ(self, *args):
        """Set the integration method.

Synopsis: MeshIm.set_integ({Integ im|int im_degree}[, ivec CVids])

Assign an integration method to all convexes whose #ids are listed in `CVids`.
If `CVids` is not given, the integration is assigned to all convexes. It is
possible to assign a specific integration method with an integration method
handle `im` obtained via Integ('IM_SOMETHING'), or to let getfem choose a
suitable integration method with `im_degree` (choosen such that polynomials of
`degree <= im_degree` are exactly integrated. If `im_degree=-1`, then the
dummy integration method IM_NONE will be used.)"""
        return self.set("integ", *args)



class MdBrick:
    """Getfem MdBrick Object.

A model brick is basically an object which modifies a global tangent
matrix and its associated right hand side. Typical modifications are
insertion of the stiffness matrix for the problem considered (linear
elasticity, laplacian, ...), handling of a set of contraints, Dirichlet
condition, addition of a source term to the right hand side, etc. The
global tangent matrix and its right hand side are stored in a MdState
object.
    """
    def __init__(self, *args):
        """General constructor for MdBrick objects.

* B = MdBrick('constraint', MdBrick pb, string CTYPE[, int nfem])
  Build a generic constraint brick. It may be useful in some situations,
  such as the Stokes problem where the pressure is defined modulo a constant.
  In such a situation, this brick can be used to add an additional constraint
  on the pressure value.
  `CTYPE` has to be chosen among 'augmented', 'penalized', and 'eliminated'.
  The constraint can be specified with MdBrick.set_constraints(). Note that
  Dirichlet bricks (except the 'generalized Dirichlet' one) are also
  specializations of the 'constraint' brick.
* B = MdBrick('dirichlet', MdBrick pb, int bnum, MeshFem mf_m, string CTYPE[, \
int nfem])
  Build a Dirichlet condition brick which impose the value of a field along a
  mesh boundary. The `bnum` parameter selects on which mesh region the
  Dirichlet condition is imposed. `CTYPE` has to be chosen among 'augmented',
  'penalized', and 'eliminated'. The `mf_m` may generally be taken as the
  MeshFem of the unknown, but for 'augmented' Dirichlet conditions, you may
  have to respect the Inf-Sup condition and choose an adequate MeshFem.
* B = MdBrick('dirichlet_on_normal_component', MdBrick pb, int bnum, MeshFem \
mf_m, string CTYPE[, int nfem])
  Build a Dirichlet condition brick which imposes the value of the normal
  component of a vector field.
* B = MdBrick('dirichlet_on_normal_derivative', MdBrick pb, int bnum, MeshFem \
mf_m, string CTYPE[, int nfem])
  Build a Dirichlet condition brick which imposes the value of the normal
  derivative of the unknown.
* B = MdBrick('generalized_dirichlet', MdBrick pb, int bnum[, int nfem])
  This is the "old" Dirichlet brick of getfem. This brick can be used to
  impose general Dirichlet conditions `h(x)u(x) = r(x)`, however it may have
  some issues with elaborated Fem's (such as Argyris, etc). It should be
  avoided when possible.
* B = MdBrick('source_term', MdBrick pb[, int bnum=-1[, int nfem]])
  Add a boundary or volumic source term ( \int B.v ). If `bnum` is omitted
  (or set to -1) , the brick adds a volumic source term on the whole mesh.
  For `bnum` >= 0, the source term is imposed on the mesh region `bnum`. Use
  MdBrick.set_param('source term',mf,B) to set the source term field. The
  source term is expected as a vector field of size Q (with Q = qdim).
* B = MdBrick('normal_source_term', MdBrick pb, int bnum[, int nfem])
  Add a boundary source term ( \int (Bn).v ). The source term is imposed on
  the mesh region `bnum` (which of course is not allowed to be a volumic
  region, only boundary regions are allowed). Use MdBrick.set_param('source
  term',mf,B) to set the source term field. The source term B is expected as
  tensor field of size QxN (with Q = qdim, N = mesh dim). For example, if you
  consider an elasticity problem, this brick may be used to impose a force on
  the boundary with B as the stress tensor.
* B = MdBrick('normal_derivative_source_term', MdBrick parent, int bnum[, int \
nfem])
  Add a boundary source term ( \int (\partial_n B).v ). The source term is
  imposed on the mesh region `bnum`. Use MdBrick.set_param('source
  term',mf,B) to set the source term field, which is expected as a vector
  field of size Q (with Q = qdim).
* B = MdBrick('neumann KirchhoffLove source term', MdBrick pb, int bnum[, int \
nfem])
  Add a boundary source term for neumann Kirchhoff-Love plate problems.
  Should be used with the Kirchhoff-Love flavour of the bilaplacian brick.
* B = MdBrick('qu_term', MdBrick pb[, int bnum[, int nfem]])
  Update the tangent matrix with a \int (Qu).v term. The Q(x) parameter is a
  matrix field of size qdim x qdim. An example of use is for the "iku" part
  of Robin boundary conditions \partial_n u + iku = ...
* B = MdBrick('mass_matrix', MeshIm mim, MeshFem mf_u[, 'real'|'complex'])
  Build a mass-matrix brick.
* B = MdBrick('generic_elliptic', MeshIm mim, MeshFem mfu[, \
'scalar'|'matrix'|'tensor'][, 'real'|'complex'])
  Setup a generic elliptic problem. a(x)*grad(U).grad(V)

  The brick parameter `a` may be a scalar field, a matrix field, or a tensor
  field (default is scalar).
* B = MdBrick('helmholtz', MeshIm mim, MeshFem mfu[, 'real'|'complex'])
  Setup a Helmholtz problem. The brick has one parameter, 'wave_number'.
* B = MdBrick('isotropic_linearized_elasticity', MeshIm mim, MeshFem mfu)
  Setup a linear elasticity problem. The brick has two scalar parameter,
  'lambda' and 'mu' (the Lame coefficients).
* B = MdBrick('linear_incompressibility_term', MdBrick pb, MeshFem mfp[, int \
nfem])
  Add an incompressibily constraint (div u = 0).
* B = MdBrick('nonlinear_elasticity', MeshIm mim, MeshFem mfu, string law)
  Setup a nonlinear elasticity (large deformations) problem.

  The material `law` can be chosen among:
  - 'SaintVenant Kirchhoff'
  Linearized material law.
  - 'Mooney Rivlin'
  To be used with the nonlinear incompressibily term.
  - 'Ciarlet Geymonat'
* B = MdBrick('nonlinear_elasticity_incompressibility_term', MdBrick pb, \
MeshFem mfp[, int nfem])
  Add an incompressibily constraint to a large strain elasticity problem.
* B = MdBrick('small_deformations_plasticity', MeshIm mim, MeshFem mfu, scalar \
THRESHOLD)
  Setup a plasticity problem (with small deformations). The `THRESHOLD`
  parameter is the maximum value of the Von Mises stress before
  'plastification' of the material.
* B = MdBrick('dynamic', MdBrick pb, scalar rho[, int numfem])
  Dynamic brick. This brick is not ready.
* B = MdBrick('bilaplacian', MeshIm mim, MeshFem mfu[, 'Kirchhoff-Love'])
  Setup a bilaplacian problem. If the 'Kirchhoff-Love' option is specified,
  the Kirchhoff-Love plate model is used.
* B = MdBrick('navier_stokes', MeshIm mim, MeshFem mfu, MeshFem mfp)
  Setup a Navier-Stokes problem (this brick is not ready, do not use it).
* B = MdBrick('isotropic_linearized_plate', MeshIm mim, MeshIm mims, MeshFem \
mfut, MeshFem mfu3, MeshFem mftheta, scalar eps)
  Setup a linear plate model brick. For moderately thick plates, using the
  Reissner-Mindlin model. `eps` is the plate thinkness, the MeshFem `mfut`
  and `mfu3` are used respectively for the membrane displacement and the
  transverse displacement of the plate. The MeshFem `mftheta` is the rotation
  of the normal ("section rotations"). The second integration method `mims`
  can be chosen equal to `mim`, or different if you want to perform sub-
  integration on the transverse shear term (mitc4 projection). This brick
  has two parameters "lambda" and "mu" (the Lame coefficients)
* B = MdBrick('mixed_isotropic_linearized_plate', MeshIm mim, MeshFem mfut, \
MeshFem mfu3, MeshFem mftheta, scalar eps)
  Setup a mixed linear plate model brick. For thin plates, using Kirchhoff-
  Love model. For a non-mixed version, use the bilaplacian brick.
* B = MdBrick('plate_source_term', MdBrick pb[, int bnum=-1[, int nfem]])
  Add a boundary or a volumic source term to a plate problem. This brick has
  two parameters: "B" is the displacement (ut and u3) source term, "M" is the
  moment source term (i.e. the source term on the rotation of the normal).
* B = MdBrick('plate_simple_support', MdBrick pb, int bnum, string CTYPE[, int \
nfem])
  Add a "simple support" boundary condition to a plate problem. Homogeneous
  Dirichlet condition on the displacement, free rotation. `CTYPE` specifies
  how the constraint is enforced ('penalized', 'augmented' or 'eliminated').
* B = MdBrick('plate_clamped_support', MdBrick pb, int bnum, string CTYPE[, \
int nfem])
  Add a "clamped support" boundary condition to a plate problem. Homogeneous
  Dirichlet condition on the displacement and on the rotation. `CTYPE`
  specifies how the constraint is enforced ('penalized', 'augmented' or
  'eliminated').
* B = MdBrick('plate_closing', MdBrick pb[, int nfem])
  Add a free edges condition for the mixed plate model brick. This brick is
  required when the mixed linearized plate brick is used. It must be inserted
  after all other boundary conditions (the reason is that the brick has to
  inspect all other boundary conditions to determine the number of
  disconnected boundary parts which are free edges).
        """
        generic_constructor(self,'mdbrick',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def get(self, *args):
        return getfem('mdbrick_get',self.id, *args)
    def set(self, *args):
        return getfem('mdbrick_set',self.id, *args)
    def __str__(self):
        return self.char()
    def __repr__(self):
        return '<getfem.MdBrick %d bytes>' % \
               (self.memsize(),)

    #@RDATTR MDBRICK:GET('nbdof')
    def nbdof(self):
        """Get the total number of dof of the current problem.

Synopsis: n = MdBrick.nbdof()

This is the sum of the brick specific dof plus the dof of the parent bricks."""
        return self.get("nbdof")


    #@RDATTR MDBRICK:GET('dim')
    def dim(self):
        """Get the dimension of the main mesh (2 for a 2D mesh, etc)."""
        return self.get("dim")


    #@RDATTR MDBRICK:GET('nb_constraints')
    def nb_constraints(self):
        """Get the total number of dof constraints of the current problem.

Synopsis: n = MdBrick.nb_constraints()

This is the sum of the brick specific dof constraints plus the dof constraints
of the parent bricks."""
        return self.get("nb_constraints")


    #@RDATTR MDBRICK:GET('is_linear')
    def is_linear(self):
        """Return true if the problem is linear."""
        return self.get("is_linear")


    #@RDATTR MDBRICK:GET('is_symmetric')
    def is_symmetric(self):
        """Return true if the problem is symmetric."""
        return self.get("is_symmetric")


    #@RDATTR MDBRICK:GET('is_coercive')
    def is_coercive(self):
        """Return true if the problem is coercive."""
        return self.get("is_coercive")


    #@RDATTR MDBRICK:GET('is_complex')
    def is_complex(self):
        """Return true if the problem uses complex numbers."""
        return self.get("is_complex")


    #@GET    MDBRICK:GET('mixed_variables') 
    def mixed_variables(self):
        """Identify the indices of mixed variables (typically the pressure, etc.) in the
tangent matrix."""
        return self.get("mixed_variables")


    #@RDATTR MDBRICK:GET('subclass')
    def subclass(self):
        """Get the typename of the brick."""
        return self.get("subclass")


    #@GET    MDBRICK:GET('param_list')
    def param_list(self):
        """Get the list of parameters names.

Synopsis: MdBrick.param_list()

Each brick embeds a number of parameters (the Lame coefficients for the
linearized elasticity brick, the wave number for the Helmholtz brick,...),
described as a (scalar, or vector, tensor etc) field on a mesh_fem. You can
read/change the parameter values with MdBrick.param() and MdBrick.set_param()."""
        return self.get("param_list")


    #@GET    MDBRICK:GET('param')
    def param(self, parameter_name):
        """Get the parameter value.

Synopsis: MdBrick.param(string parameter_name)

When the parameter has been assigned a specific MeshFem, it is returned as a
large array (the last dimension being the MeshFem dof). When no MeshFem has
been assigned, the parameter is considered to be constant over the mesh."""
        return self.get("param", parameter_name)


    #@GET    MDBRICK:GET('solve')
    def solve(self, mds, *args):
        """Run the standard getfem solver.

Synopsis: MdBrick.solve(MdState mds[,...])

Note that you should be able to use your own solver if you want (it is
possible to obtain the tangent matrix and its right hand side with the
MdState.tangent_matrix() etc.).

Various options can be specified:

- 'noisy' or 'very noisy'
  the solver will display some information showing the progress
  (residual values etc.).
- 'max_iter', NIT
  set the maximum iterations numbers.
- 'max_res', RES
  set the target residual value.
- 'lsolver', SOLVERNAME
  select explicitely the solver used for the linear systems (the
  default value is 'auto', which lets getfem choose itself).
  Possible values are 'superlu', 'mumps' (if supported),
  'cg/ildlt', 'gmres/ilu' and 'gmres/ilut'."""
        return self.get("solve", mds, *args)


    #@GET    MDBRICK:GET('von mises')
    def von_mises(self, mds, mfvm):
        """Compute the Von Mises stress on the MeshFem `mfvm`.

Synopsis: VM = MdBrick.von_mises(MdState mds, MeshFem mfvm)

Only available on bricks where it has a meaning: linearized elasticity,
plasticity, nonlinear elasticity. Note that in 2D it is not the "real" Von
Mises (which should take into account the 'plane stress' or 'plane strain'
aspect), but a pure 2D Von Mises."""
        return self.get("von mises", mds, mfvm)


    #@GET    MDBRICK:GET('tresca')
    def tresca(self, mds, mft):
        """Compute the Tresca stress criterion on the MeshFem `mft`.

Synopsis: T = MdBrick.tresca(MdState mds, MeshFem mft)

Only available on bricks where it has a meaning: linearized elasticity,
plasticity, nonlinear elasticity."""
        return self.get("tresca", mds, mft)


    #@GET    MDBRICK:GET('memsize')
    def memsize(self):
        """Return the amount of memory (in bytes) used by the model brick."""
        return self.get("memsize")



    #@SET    MDBRICK:SET('param')
    def set_param(self, name, *args):
        """Change the value of a brick parameter.

Synopsis: MdBrick.set_param(string name,{MeshFem mf,V | V})

`name` is the name of the parameter. `V` should contain the new parameter
value (vector or float). If a MeshFem is given, `V` should hold the field
values over that MeshFem (i.e. its last dimension should be MeshFem.nbdof() or
1 for constant field)."""
        return self.set("param", name, *args)


    #@SET    MDBRICK:SET('penalization_epsilon');
    def penalization_epsilon(self, eps):
        """Change the penalization coefficient of a constraint brick.

Synopsis: MdBrick.penalization_epsilon(scalar eps)

This is only applicable to the bricks which inherit from the constraint brick,
such as the Dirichlet ones. And of course it is not effective when the
constraint is enforced via direct elimination or via Lagrange multipliers. The
default value of `eps` is 1e-9."""
        return self.set("penalization_epsilon", eps)


    #@SET    MDBRICK:SET('constraints');
    def set_constraints(self, H, R):
        """Set the constraints imposed by a constraint brick.

Synopsis: MdBrick.set_constraints(mat H, vec R)

This is only applicable to the bricks which inherit from the constraint brick,
such as the Dirichlet ones. Imposes `H.U=R`."""
        return self.set("constraints", H, R)


    #@SET    MDBRICK:SET('constraints_rhs');
    def set_constraints_rhs(self, H, R):
        """Set the right hand side of the constraints imposed by a constraint brick.

Synopsis: MdBrick.set_constraints_rhs(mat H, vec R)

This is only applicable to the bricks which inherit from the constraint brick,
such as the Dirichlet ones."""
        return self.set("constraints_rhs", H, R)


class MdState:
    """Getfem MdState Object.

A model state is an object which store the state data for a chain of
model bricks. This includes the global tangent matrix, the right hand
side and the constraints.
    """
    def __init__(self, *args):
        """General constructor for MdState objects.
There are two sorts of model states, the 'real' and the 'complex'
model states.

* MDS = MdState(MdBrick B)
  Build a modelstate for the brick `B`. Selects the real or complex state
  from the complexity of `B`.
* MDS = MdState('real')
  Build a model state for real unknowns.
* MDS = MdState('complex')
  Build a model state for complex unknowns.
        """
        generic_constructor(self,'mdstate',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def get(self, *args):
        return getfem('mdstate_get',self.id, *args)
    def set(self, *args):
        return getfem('mdstate_set',self.id, *args)
    def __str__(self):
        return self.char()
    def __repr__(self):
        return '<getfem.MdState %d bytes>' % \
               (self.memsize(),)

    #@RDATTR MDSTATE:GET('is_complex')
    def is_complex(self):
        """Return 0 is the model state is real, 1 if it is complex."""
        return self.get("is_complex")


    #@GET    MDSTATE:GET('tangent_matrix')
    def tangent_matrix(self):
        """Return the tangent matrix stored in the model state."""
        return self.get("tangent_matrix")


    #@GET    MDSTATE:GET('constraints_matrix')
    def constraints_matrix(self):
        """Return the constraints matrix stored in the model state."""
        return self.get("constraints_matrix")


    #@GET    MDSTATE:GET('reduced_tangent_matrix')
    def reduced_tangent_matrix(self):
        """Return the reduced tangent matrix (i.e. the tangent matrix after elimination
of the constraints)."""
        return self.get("reduced_tangent_matrix")


    #@GET    MDSTATE:GET('constraints_nullspace')
    def constraints_nullspace(self):
        """Return the nullspace of the constraints matrix."""
        return self.get("constraints_nullspace")


    #@GET    MDSTATE:GET('state')
    def state(self):
        """Return the vector of unknowns, which contains the solution after
MdBrick.solve()."""
        return self.get("state")


    #@GET    MDSTATE:GET('residual')
    def residual(self):
        """Return the residual."""
        return self.get("residual")


    #@GET    MDSTATE:GET('reduced_residual')
    def reduced_residual(self):
        """Return the residual on the reduced system."""
        return self.get("reduced_residual")


    #@GET    MDSTATE:GET('unreduce')
    def unreduce(self, U):
        """Reinsert the constraint eliminated from the system."""
        return self.get("unreduce", U)


    #@GET    MDSTATE:GET('memsize')
    def memsize(self):
        """Return the amount of memory (in bytes) used by the model state."""
        return self.get("memsize")



    #@SET    MDSTATE:SET('compute_reduced_system')
    def compute_reduced_system(self):
        """Compute the reduced system from the tangent matrix and constraints."""
        return self.set("compute_reduced_system")


    #@SET    MDSTATE:SET('compute_reduced_residual')
    def compute_reduced_residual(self):
        """Compute the reduced residual from the residual and constraints."""
        return self.set("compute_reduced_residual")


    #@SET    MDSTATE:SET('compute_residual')
    def compute_residual(self, B):
        """Compute the residual for the brick `B`."""
        return self.set("compute_residual", B)


    #@SET    MDSTATE:SET('compute_tangent_matrix')
    def compute_tangent_matrix(self, B):
        """Update the tangent matrix from the brick `B`."""
        return self.set("compute_tangent_matrix", B)


    #@SET    MDSTATE:SET('state')
    def set_state(self, U):
        """Update the internal state with the vector `U`."""
        return self.set("state", U)


    #@SET    MDSTATE:SET('clear')
    def clear(self):
        """Clear the model state."""
        return self.set("clear")



class Model:
    """Getfem Model Object.

A model is an object which store all the state variable and the data of a
model and a list of bricks. A brick is a component of the model, i.e. a
term wich link some state variables. The model object  includes the global
tangent matrix, the right hand side and the constraints.
    """
    def __init__(self, *args):
        """General constructor for Model objects.
There are two sorts of model states, the 'real' and the 'complex'
model states.

* MDS = Model('real')
  Build a model for real unknowns.
* MDS = Model('complex')
  Build a model for complex unknowns.
        """
        generic_constructor(self,'model',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def get(self, *args):
        return getfem('model_get',self.id, *args)
    def set(self, *args):
        return getfem('model_set',self.id, *args)
    def __str__(self):
        return self.char()
    def __repr__(self):
        return '<getfem.Model %d bytes>' % \
               (self.memsize(),)

    #@RDATTR MODEL:GET('is_complex')
    def is_complex(self):
        """Return 0 is the model is real, 1 if it is complex."""
        return self.get("is_complex")


    #@GET    MODEL:GET('tangent_matrix')
    def tangent_matrix(self):
        """Return the tangent matrix stored in the model ."""
        return self.get("tangent_matrix")


    #@GET    MODEL:GET('rhs')
    def rhs(self):
        """Return the right hand side of the tangent problem."""
        return self.get("rhs")


    #@GET    MODEL:GET('memsize')
    def memsize(self):
        """Return a rough approximation of the amount of memory (in bytes) used by the
model."""
        return self.get("memsize")


    #@GET    MODEL:GET('listvar')
    def listvar(self):
        """print to the output the list of variables and constants of the model."""
        return self.get("listvar")


    #@GET    MODEL:GET('listbricks')
    def listbricks(self):
        """print to the output the list of bricks of the model."""
        return self.get("listbricks")


    #@GET    MODEL:GET('variable')
    def variable(self, name, niter=None):
        """Gives the value of a variable or data."""
        return self.get("variable", name, niter)


    #@GET    MODEL:GET('mult varname Dirichlet')
    def mult_varname_Dirichlet(self, ind_brick):
        """Gives the name of the multiplier variable for a Dirichlet brick. If the brick
is not a Dirichlet condition with multiplier brick, this function has an
undefined behavior"""
        return self.get("mult varname Dirichlet", ind_brick)


    #@GET    MODEL:GET('from variables')
    def from_variables(self):
        """Return the vector of all the degrees of freedom of the model consisting of the
concatenation of the variables of the model (usefull to solve your problem
with you own solver)."""
        return self.get("from variables")


    #@GET    MODEL:GET('assembly')
    def assembly(self, option=None):
        """Assembly of the tangent system taking into account the terms from all bricks.
`option`, if specified, should be 'build all', 'build rhs' or 'build matrix'.
The default is to build the whole tangent linear system (matrix and rhs). This
function is usefull to solve your problem with you own solver."""
        return self.get("assembly", option)


    #@GET    MODEL:GET('solve')
    def solve(self, *args):
        """Run the standard getfem solver.

Synopsis: Model.solve([,...])

Note that you should be able to use your own solver if you want (it is
possible to obtain the tangent matrix and its right hand side with the
Model.tangent_matrix() etc.).

Various options can be specified:

- 'noisy' or 'very noisy'
  the solver will display some information showing the progress
  (residual values etc.).
- 'max_iter', NIT
  set the maximum iterations numbers.
- 'max_res', RES
  set the target residual value.
- 'lsolver', SOLVERNAME
  select explicitely the solver used for the linear systems (the
  default value is 'auto', which lets getfem choose itself).
  Possible values are 'superlu', 'mumps' (if supported),
  'cg/ildlt', 'gmres/ilu' and 'gmres/ilut'."""
        return self.get("solve", *args)


    #@GET    MODEL:GET('compute isotropic linearized Von Mises or Tresca')
    def compute_isotropic_linearized_Von_Mises_or_Tresca(self, varname, dataname_lambda, dataname_mu, mf_vm, version=None):
        """Compute the Von-Mises stress or the Tresca stress of a field (only valid for
isotropic linearized elasticity in 3D). `version` should be 'Von Mises' or
'Tresca' ('Von Mises' is the default)."""
        return self.get("compute isotropic linearized Von Mises or Tresca", varname, dataname_lambda, dataname_mu, mf_vm, version)




    #@SET    MODEL:SET('variable')
    def set_variable(self, name, V, niter=None):
        """Set the value of a variable or data."""
        return self.set("variable", name, V, niter)


    #@SET    MODEL:SET('clear')
    def clear(self):
        """Clear the model."""
        return self.set("clear")


    #@SET    MODEL:SET('add fem variable')
    def add_fem_variable(self, name, mf, niter=None):
        """Add a variable to the model linked to a MeshFem. `name` is the variable name
and `niter` is the optional number of copy of the variable for time
integration schemes."""
        return self.set("add fem variable", name, mf, niter)


    #@SET    MODEL:SET('add variable')
    def add_variable(self, name, size, niter=None):
        """Add a variable to the model of constant size. `name` is the variable name and
`niter` is the optional number of copy of the variable for time integration
schemes."""
        return self.set("add variable", name, size, niter)


    #@SET    MODEL:SET('add multiplier')
    def add_multiplier(self, name, mf, primalname, niter=None):
        """Add a particular variable linked to a fem being a multiplier with respect to a
primal variable. The dof will be filtered with the gmm::range_basis function
applied on the terms of the model which link the multiplier and the primal
variable. This in order to retain only linearly independant constraints on the
primal variable. Optimized for boundary multipliers. niter is the number of
version of the data stored, for time integration schemes."""
        return self.set("add multiplier", name, mf, primalname, niter)


    #@SET    MODEL:SET('add fem data')
    def add_fem_data(self, name, mf, qdim=None, niter=None):
        """Add a data to the model linked to a MeshFem. `name` is the data name, `qdim`
is the optional dimension of the data over the MeshFem and `niter` is the
optional number of copy of the data for time integration schemes."""
        return self.set("add fem data", name, mf, qdim, niter)


    #@SET    MODEL:SET('add initialized fem data')
    def add_initialized_fem_data(self, name, mf, V):
        """Add a data to the model linked to a MeshFem. `name` is the data name. The data
is initiakized with `V`. The data can be a scalar or vector field."""
        return self.set("add initialized fem data", name, mf, V)


    #@SET    MODEL:SET('add data')
    def add_data(self, name, size, niter=None):
        """Add a data to the model of constant size. `name` is the data name and `niter`
is the optional number of copy of the data for time integration schemes."""
        return self.set("add data", name, size, niter)


    #@SET    MODEL:SET('add initialized data')
    def add_initialized_data(self, name, V):
        """Add a fixed size data to the model linked to a MeshFem. `name` is the data
name, `V` is the value of the data."""
        return self.set("add initialized data", name, V)


    #@SET    MODEL:SET('to variables')
    def to_variables(self, V):
        """Set the value of the variables of the model with the vector `V`. Typically,
the vector `V` results of the solve of the tangent linear system (usefull to
solve your problem with you own solver)."""
        return self.set("to variables", V)


    #@SET    MODEL:SET('add Laplacian brick')
    def add_Laplacian_brick(self, mim, varname, region=None):
        """add a Laplacian term to the model relatively to the variable `varname`. If
this is a vector valued variable, the Laplacian term is added componentwise.
`region` is an optional mesh region on which the term is added. If it is not
specified, it is added on the whole mesh."""
        return self.set("add Laplacian brick", mim, varname, region)


    #@SET    MODEL:SET('add generic elliptic brick')
    def add_generic_elliptic_brick(self, mim, varname, dataname, region=None):
        """add a generic elliptic term to the model relatively to the variable `varname`.
The shape of the elliptic term depends both on the variable and the data. This
corresponds to a term $-\text{div}(a\nabla u)$ where $a$ is the data and $u$
the variable. The data can be a scalar, a matrix or an order four tensor. The
variable can be vector valued or not. If the data is a scalar or a matrix and
the variable is vector valued then the term is added componentwise. An order
four tensor data is allowed for vector valued variable only. The data can be
constant or describbed on a fem. Of course, when the data is a tensor describe
on a finite element method (a tensor field) the data can be a huge vector. The
components of the matrix/tensor have to be stored with the fortran order
(columnwise) in the data vector (compatibility with blas). The symmetry of the
given matrix/tensor is not verified (but assumed). If this is a vector valued
variable, the Laplacian term is added componentwise. `region` is an optional
mesh region on which the term is added. If it is not specified, it is added
on the whole mesh."""
        return self.set("add generic elliptic brick", mim, varname, dataname, region)


    #@SET    MODEL:SET('add source term brick')
    def add_source_term_brick(self, mim, varname, dataname, region=None):
        """add a source term to the model relatively to the variable `varname`. The
source term is represented by the data `dataname` which could be constant or
described on a fem. `region` is an optional mesh region on which the term is
added. An additional optional data `directdataname` can be provided. The
corresponding data vector will be directly added to the right hand side
without assembly."""
        return self.set("add source term brick", mim, varname, dataname, region)


    #@SET    MODEL:SET('add normal source term brick')
    def add_normal_source_term_brick(self, mim, varname, dataname, region=None):
        """add a source term on the variable `varname` on a boundary `region`. The source
term is represented by the data `dataname` which could be constant or
described on a fem. A scalar product with the outward normal unit vector to
the boundary is performed. The main aim of this brick is to represent a
Neumann condition with a vector data without performing the scalar product
with the normal as a pre-processing."""
        return self.set("add normal source term brick", mim, varname, dataname, region)


    #@SET    MODEL:SET('add Dirichlet condition with multipliers')
    def add_Dirichlet_condition_with_multipliers(self, mim, varname, mult_description, region, dataname=None):
        """Add a Dirichlet condition on the variable `varname` and the mesh region
`region`. This region should be a boundary. The Dirichlet condition is
prescribed with a multiplier variable described by `mult_description`. If
`mult_description` is a string this is assumed to be the variable name
correpsonding to the multiplier (which should be first declared as a
multiplier variable on the mesh region in the model). If it is a finite
element method (mesh_fem object) then a multiplier variable will be added to
the model and build on this finite element method (it will be restricted to
the mesh region `region` and eventually some conflicting dofs with some other
multiplier variables will be suppressed). If it is an integer, then a
multiplier variable will be added to the model and build on a classical finite
element of degree that integer. `dataname` is the optional right hand side of
the Dirichlet condition. It could be constant or described on a fem; scalar or
vector valued, depending on the variable on which the Dirichlet condition is
prescribed. Return the brick index in the model."""
        return self.set("add Dirichlet condition with multipliers", mim, varname, mult_description, region, dataname)


    #@SET    MODEL:SET('add Dirichlet condition with penalization')
    def add_Dirichlet_condition_with_penalization(self, mim, varname, coeff, region, dataname=None):
        """Add a Dirichlet condition on the variable `varname` and the mesh region
`region`. This region should be a boundary. The Dirichlet condition is
prescribed with penalization. The penalization coefficient is intially `coeff`
and will be added to the data of the model. `dataname` is the optional right
hand side of the Dirichlet condition. It could be constant or described on a
fem; scalar or vector valued, depending on the variable on which the Dirichlet
condition is prescribed. Return the brick index in the model."""
        return self.set("add Dirichlet condition with penalization", mim, varname, coeff, region, dataname)


    #@SET    MODEL:SET('change penalization coeff')
    def change_penalization_coeff(self, ind_brick, coeff):
        """Change the penalization coefficient of a Dirichlet condition with penalization
brick. If the brick is not of this kind, this function has an undefined
behavior."""
        return self.set("change penalization coeff", ind_brick, coeff)


    #@SET    MODEL:SET('add Helmholtz brick')
    def add_Helmholtz_brick(self, mim, varname, dataname, region=None):
        """add a Helmholtz term to the model relatively to the variable `varname`.
`dataname` should contain the wave number. `region` is an optional mesh region
on which the term is added. If it is not specified, it is added on the whole
mesh."""
        return self.set("add Helmholtz brick", mim, varname, dataname, region)


    #@SET    MODEL:SET('add Fourier Robin brick')
    def add_Fourier_Robin_brick(self, mim, varname, dataname, region):
        """add a Fourier-Robin term to the model relatively to the variable `varname`.
this corresponds to a weak term of the form $\int (qu).v$. `dataname` should
contain the parameter $q$ of the Fourier-Robin condition. `region` is the mesh
region on which the term is added."""
        return self.set("add Fourier Robin brick", mim, varname, dataname, region)


    #@SET    MODEL:SET('add constraint with multipliers')
    def add_constraint_with_multipliers(self, varname, multname, B, L):
        """Add an additional explicit constraint on the variable `varname` thank to a
multiplier `multname` peviously added to the model (should be a fixed size
variable). The constraint is $BU=L$ with `B` being a rectangular sparse
matrix. It is possible to change the constraint at any time whith the methods
Model.set_private_matrix() and Model.set_private_rhs()"""
        return self.set("add constraint with multipliers", varname, multname, B, L)


    #@SET    MODEL:SET('add constraint with penalization')
    def add_constraint_with_penalization(self, varname, coeff, B, L):
        """Add an additional explicit penalized constraint on the variable `varname`. The
constraint is $BU=L$ with `B` being a rectangular sparse matrix. Be aware that
`B` should not contain a palin row, otherwise the whole tangent matrix will be
plain. It is possible to change the constraint at any time whith the methods
Model.set_private_matrix() and Model.set_private_rhs(). The method
Model.change_penalization_coeff() can be used."""
        return self.set("add constraint with penalization", varname, coeff, B, L)


    #@SET    MODEL:SET('add explicit matrix')
    def add_explicit_matrix(self, varname1, varname2, B, issymmetric=None, *args):
        """Add a brick reprenting an explicit matrix to be added to the tangent linear
system relatively to the variables 'varname1' and 'varname2'. The given matrix
should have has many rows as the dimension of 'varname1' and as many columns
as the dimension of 'varname2'. If the two variables are different and if
`issymmetric' is set to 1 then the transpose of the matrix is also added to
the tangent system (default is 0). set `iscoercive` to 1 if the term does not
affect the coercivity of the tangent system (default is 0). The matrix can be
changed by the command Model.set_private_matrix()."""
        return self.set("add explicit matrix", varname1, varname2, B, issymmetric, *args)


    #@SET    MODEL:SET('add explicit rhs')
    def add_explicit_rhs(self, varname, L):
        """Add a brick reprenting an explicit right hand side to be added to the right
hand side of the tangent linear system relatively to the variable 'varname'.
The given rhs should have the same size than the dimension of 'varname'. The
rhs can be changed by the command Model.set_private_rhs()."""
        return self.set("add explicit rhs", varname, L)


    #@SET    MODEL:SET('set private matrix')
    def set_private_matrix(self, indbrick, B):
        """For some specific bricks having an internal sparse matrix (explicit bricks:
'constraint brick' and 'explicit matrix brick'), set this matrix."""
        return self.set("set private matrix", indbrick, B)


    #@SET    MODEL:SET('set private rhs')
    def set_private_rhs(self, indbrick, B):
        """For some specific bricks having an internal right hand side vector (explicit
bricks: 'constraint brick' and 'explicit rhs brick'), set this rhs."""
        return self.set("set private rhs", indbrick, B)


    #@SET    MODEL:SET('disable bricks')
    def disable_bricks(self, bricks_indices):
        """Disable a brick (the brick will no longer participate to the building of the
tangent linear system)."""
        return self.set("disable bricks", bricks_indices)


    #@SET    MODEL:SET('unable bricks')
    def unable_bricks(self, bricks_indices):
        """Unable a disabled brick."""
        return self.set("unable bricks", bricks_indices)


    #@SET    MODEL:SET('add isotropic linearized elasticity brick')
    def add_isotropic_linearized_elasticity_brick(self, mim, varname, dataname_lambda, dataname_mu, region=None):
        """add an isotropic linearized elasticity term to the model relatively to the
variable `varname`. `dataname_lambda` and `dataname_mu` should contain the
Lam\'e coefficients. `region` is an optional mesh region on which the term is
added. If it is not specified, it is added on the whole mesh."""
        return self.set("add isotropic linearized elasticity brick", mim, varname, dataname_lambda, dataname_mu, region)


    #@SET    MODEL:SET('add linear incompressibility brick')
    def add_linear_incompressibility_brick(self, mim, varname, multname_pressure, region=None, *args):
        """add an linear incompressibility condition on `variable`. `multname_pressure`
is a variable which represent the pressure. Be aware that an inf-sup condition
between the finite element method describing the rpressure and the primal
variable has to be satisfied. `region` is an optional mesh region on which the
term is added. If it is not specified, it is added on the whole mesh.
`dataname_coeff` is an optional penalization coefficient for nearly
incompressible elasticity for instance. In this case, it is the inverse of the
Lam\'e coefficient $\lambda$."""
        return self.set("add linear incompressibility brick", mim, varname, multname_pressure, region, *args)


    #@SET    MODEL:SET('add mass brick')
    def add_mass_brick(self, mim, varname, dataname_rho=None, *args):
        """add mass term to the model relatively to the variable `varname`. If specified,
the data `dataname_rho` should contain the density (1 if omitted). `region` is
an optional mesh region on which the term is added. If it is not specified, it
is added on the whole mesh."""
        return self.set("add mass brick", mim, varname, dataname_rho, *args)


    #@SET    MODEL:SET('add basic d on dt brick')
    def add_basic_d_on_dt_brick(self, mim, varnameU, dataname_dt, dataname_rho=None, *args):
        """Add the standard discretization of a first order time derivative on
`varnameU`. The parameter $rho$ is the density which could be omitted (the
defaul value is 1). This brick should be used in addition to a time dispatcher
for the other terms."""
        return self.set("add basic d on dt brick", mim, varnameU, dataname_dt, dataname_rho, *args)


    #@SET    MODEL:SET('add basic d2 on dt2 brick')
    def add_basic_d2_on_dt2_brick(self, mim, varnameU, datanameV, dataname_dt, dataname_alpha, dataname_rho=None, *args):
        """Add the standard discretization of a second order time derivative on
`varnameU`. `datanameV` is a data represented on the same finite element
method as U which represents the time derivative of U. The parameter $rho$ is
the density which could be omitted (the defaul value is 1). This brick should
be used in addition to a time dispatcher for the other terms. The time
derivative $v$ of the variable $u$ is preferably computed as a post-traitement
which depends on each scheme. The parameter `dataname_alpha` depends on the
time integration scheme."""
        return self.set("add basic d2 on dt2 brick", mim, varnameU, datanameV, dataname_dt, dataname_alpha, dataname_rho, *args)


    #@SET    MODEL:SET('add theta method dispatcher')
    def add_theta_method_dispatcher(self, bricks_indices, theta):
        """Add a theta-method time dispatcher to a list of bricks. For instance, a matrix
term $K$ will be replaced by $\theta K U^{n+1} + (1-\theta) K U^{n}$."""
        return self.set("add theta method dispatcher", bricks_indices, theta)


    #@SET    MODEL:SET('velocity update for order two theta method')
    def velocity_update_for_order_two_theta_method(self, varnameU, datanameV, dataname_dt, dataname_theta):
        """Function which udpate the velocity $v^{n+1}$ after the computation of the
displacement $u^{n+1}$ and before the next iteration. Specific for theta-
method and when the velocity is included in the data of the model."""
        return self.set("velocity update for order two theta method", varnameU, datanameV, dataname_dt, dataname_theta)


    #@SET    MODEL:SET('add midpoint dispatcher')
    def add_midpoint_dispatcher(self, bricks_indices):
        """Add a midpoint time dispatcher to a list of bricks. For instance, a nonlinear
term $K(U)$ will be replaced by $K((U^{n+1} + U^{n})/2)$."""
        return self.set("add midpoint dispatcher", bricks_indices)


    #@SET    MODEL:SET('velocity update for Newmark scheme')
    def velocity_update_for_Newmark_scheme(self, id2dt2_brick, varnameU, datanameV, dataname_dt, dataname_twobeta, dataname_alpha):
        """Function which udpate the velocity $v^{n+1}$ after the computation of the
displacement $u^{n+1}$ and before the next iteration. Specific for Newmark
scheme and when the velocity is included in the data of the model. This
version inverts the mass matrix by a conjugate gradient."""
        return self.set("velocity update for Newmark scheme", id2dt2_brick, varnameU, datanameV, dataname_dt, dataname_twobeta, dataname_alpha)


    #@SET    MODEL:SET('first iter')
    def first_iter(self):
        """To be executed before the first iteration of a time integration scheme."""
        return self.set("first iter")


    #@SET    MODEL:SET('next iter')
    def next_iter(self):
        """To be executed at the end of each iteration of a time integration scheme."""
        return self.set("next iter")


class GeoTrans:
    """General function for building descriptors to geometric transformations.

The geometric transformation must be used when you are building
a custom mesh convex by convex (see the add_convex() function of 
getfem.Mesh): it also defines the kind of convex (triangle,
hexahedron, prism, etc..)    
    """
    def __init__(self, *args):
        """Build a GeoTrans object from a string description.

GeoTrans = GeoTrans(string name)

The name argument contains the specification of the geometric
transformation as a string, which may be:

* GT_PK(n,k)
  Transformation on simplexes, dim `n`, degree `k`.
* GT_QK(n,k)
  Transformation on parallelepipeds, dim `n`, degree `k`.
* GT_PRISM(n,k)
  Transformation on prisms, dim `n`, degree `k`.
* GT_PRODUCT(A,B)
  Tensorial product of two transformations.
* GT_LINEAR_PRODUCT(A,B)
  Linear tensorial product of two transformations
        """
        generic_constructor(self,'geotrans',*args)
    def __del__(self):
        generic_destructor(self,destructible=False)
    def get(self, *args):
        return getfem('geotrans_get',self.id, *args)
    def __str__(self):
        return self.get('char')
    def __repr__(self):
        return '<getfem.Geotrans '+str(self)+'>'

    #@RDATTR GEOTRANS:GET('dim')
    def dim(self):
        """Get the dimension of the GeoTrans.

Synopsis: d = GeoTrans.dim()

This is the dimension of the source space, i.e. the dimension of the reference
convex."""
        return self.get("dim")


    #@RDATTR GEOTRANS:GET('is_linear')
    def is_linear(self):
        """Return 0 if the GeoTrans is not linear."""
        return self.get("is_linear")


    #@RDATTR GEOTRANS:GET('nbpts')
    def nbpts(self):
        """Return the number of points of the GeoTrans."""
        return self.get("nbpts")


    #@GET    GEOTRANS:GET('pts')
    def pts(self):
        """Return the reference convex points of the GeoTrans.

Synopsis: P = GeoTrans.pts()

The points are stored in the columns of the output matrix."""
        return self.get("pts")


    #@GET    GEOTRANS:GET('normals')
    def normals(self):
        """Get the normals for each face of the reference convex of the GeoTrans.

Synopsis: N = GeoTrans.normals()

The normals are stored in the columns of the output matrix."""
        return self.get("normals")


    #@GET    GEOTRANS:GET('transform')
    def transform(self, G, Pr):
        """Apply the GeoTrans to a set of points.

Synopsis: Pt = GeoTrans.transform(mat G, mat Pr)

`G` is the set of vertices of the real convex, `Pr` is the set of points (in
the reference convex) that are to be transformed. The corresponding set of
points in the real convex is returned."""
        return self.get("transform", G, Pr)


    #@GET    GEOTRANS:GET('char')
    def char(self):
        """Output a (unique) string representation of the GeoTrans.

Synopsis: s = GeoTrans.char()

This can be used to perform comparisons between two different GeoTrans
objects."""
        return self.get("char")


class Fem:
    """FEM (Finite Element Method) objects."""
    def __init__(self, fem_name):
        """Build a FEM object from a string description.

Fem = Fem(string fem_name)

The `fem_name` should contain a description of the finite element method.
Please refer to the getfem++ manual (especially the description of finite
element and integration methods) for a complete reference. Here is a list
of some of them:

* FEM_PK(n,k)
  classical Lagrange element Pk on a simplex of dimension `n`.
* FEM_PK_DISCONTINUOUS(N,K[,alpha])
  discontinuous Lagrange element Pk on a simplex of dimension `n`.
* FEM_QK(n,k)
  classical Lagrange element Qk on quadrangles, hexahedrons etc.
* FEM_QK_DISCONTINUOUS(n,k[,alpha])
  discontinuous Lagrange element Qk on quadrangles, hexahedrons etc.
* FEM_Q2_INCOMPLETE
  incomplete 2D Q2 element with 8 dof (serendipity Quad 8 element).
* FEM_PK_PRISM(n,k)
  classical Lagrange element Pk on a prism.
* FEM_PK_PRISM_DISCONTINUOUS(n,k[,alpha])
  classical discontinuous Lagrange element Pk on a prism.
* FEM_PK_WITH_CUBIC_BUBBLE(n,k)
  classical Lagrange element Pk on a simplex with an additional volumic
  bubble function.
* FEM_P1_NONCONFORMING
  non-conforming P1 method on a triangle.
* FEM_P1_BUBBLE_FACE(n)
  P1 method on a simplex with an additional bubble function on face 0.
* FEM_P1_BUBBLE_FACE_LAG
  P1 method on a simplex with an additional lagrange dof on face 0.
* FEM_PK_HIERARCHICAL(n,k)
  PK element with a hierarchical basis.
* FEM_QK_HIERARCHICAL(n,k)
  QK element with a hierarchical basis
* FEM_PK_PRISM_HIERARCHICAL(n,k)
  PK element on a prism with a hierarchical basis.
* FEM_STRUCTURED_COMPOSITE(FEM,k)
  Composite fem on a grid with `k` divisions.
* FEM_PK_HIERARCHICAL_COMPOSITE(n,k,s)
  Pk composite element on a grid with `s` subdivisions and with a
  hierarchical basis.
* FEM_PK_FULL_HIERARCHICAL_COMPOSITE(n,k,s)
  Pk composite element with `s` subdivisions and a hierarchical basis
  on both degree and subdivision.
* FEM_PRODUCT(FEM1,FEM2)
  tensorial product of two polynomial elements.
* FEM_HERMITE(n)
  Hermite element P3 on a simplex of dimension `n = 1, 2, 3`.
* FEM_ARGYRIS
  Argyris element P5 on the triangle.
* FEM_HCT_TRIANGLE
  Hsieh-Clough-Tocher element on the triangle (composite P3
  element which is C^1), should be used with IM_HCT_COMPOSITE()
  integration method.
* FEM_QUADC1_COMPOSITE
  Quadrilateral element, composite P3 element and C^1 (16 dof).
* FEM_REDUCED_QUADC1_COMPOSITE
  Quadrilateral element, composite P3 element and C^1 (12 dof).
* FEM_RT0(n)
  Raviart-Thomas element of order 0 on a simplex of dimension `n`.
* FEM_NEDELEC(n)
  Nedelec edge element of order 0 on a simplex of dimension `n`.

Of course, you have to ensure that the selected fem is compatible with
the geometric transformation: a Pk fem has no meaning on a quadrangle.

**SPECIAL FEM:**

* Fem = Fem('interpolated_fem', MeshFem mf, MeshIm mim, [ivec blocked_dof])
  Build a special Fem which is interpolated from another MeshFem.

  Using this special finite element, it is possible to interpolate a given
  MeshFem `mf` on another mesh, given the integration method `mim` that will
  be used on this mesh.

  Note that this finite element may be quite slow, and eats much memory.
        """
        generic_constructor(self,'fem',fem_name)
    def __del__(self):
        generic_destructor(self,destructible=False)
    def get(self, *args):
        return getfem('fem_get',self.id, *args)
    def __str__(self):
        return self.get('char')
    def __repr__(self):
        return '<getfem.Fem '+str(self)+'>'

    #@RDATTR FEM:GET('nbdof')
    def nbdof(self, cv=None):
        """Return the number of dof for the Fem.

Synopsis: n = Fem.nbdof([, int cv])

Some specific Fem (for example 'interpolated_fem') may require a convex number
`cv` to give their result. In most of the case, you can omit this convex
number."""
        return self.get("nbdof", cv)


    #@RDATTR FEM:GET('dim')
    def dim(self):
        """Return the dimension (dimension of the reference convex) of the Fem."""
        return self.get("dim")


    #@RDATTR FEM:GET('target_dim')
    def target_dim(self):
        """Return the dimension of the target space.

Synopsis: td = Fem.target_dim()

The target space dimension is usually 1, except for vector Fem (none of them
has been implemented in getfem++ for now)."""
        return self.get("target_dim")


    #@GET    FEM:GET('pts')
    def pts(self, cv=None):
        """Get the location of the dof on the reference element.

Synopsis: P = Fem.pts([, int cv])

Some specific Fem may require a convex number `cv` to give their result (for
example 'interpolated_fem'). In most of the case, you can omit this convex
number."""
        return self.get("pts", cv)


    #@RDATTR FEM:GET('is_equivalent')
    def is_equivalent(self):
        """Return 0 if the Fem is not equivalent.

Synopsis: b = Fem.is_equivalent()

Equivalent Fem are evaluated on the reference convex. This is the case of most
classical Fem's."""
        return self.get("is_equivalent")


    #@RDATTR FEM:GET('is_lagrange')
    def is_lagrange(self):
        """Return 0 if the Fem is not of Lagrange type."""
        return self.get("is_lagrange")


    #@RDATTR FEM:GET('is_polynomial')
    def is_polynomial(self):
        """Return 0 if the basis functions are not polynomials."""
        return self.get("is_polynomial")


    #@RDATTR FEM:GET('estimated_degree')
    def estimated_degree(self):
        """Return an estimation of the polynomial degree of the Fem.

Synopsis: d = Fem.estimated_degree()

This is an estimation for fem which are not polynomials."""
        return self.get("estimated_degree")


    #@GET    FEM:GET('base_value')
    def base_value(self, p):
        """Evaluate all basis functions of the FEM at point `p`.

Synopsis: E = Fem.base_value(mat p)

`p` is supposed to be in the reference convex!"""
        return self.get("base_value", p)


    #@GET    FEM:GET('grad_base_value')
    def grad_base_value(self, p):
        """Evaluate the gradient of all base functions of the Fem at point `p`.

Synopsis: ED = Fem.grad_base_value(mat p)

`p` is supposed to be in the reference convex!"""
        return self.get("grad_base_value", p)


    #@GET    FEM:GET('hess_base_value')
    def hess_base_value(self, p):
        """Evaluate the Hessian of all base functions of the Fem at point `p`.

Synopsis: EH = Fem.hess_base_value(mat p)

`p` is supposed to be in the reference convex!."""
        return self.get("hess_base_value", p)


    #@GET    FEM:GET('poly_str')
    def poly_str(self):
        """Return the polynomial expressions of its basis functions in the reference
convex.

Synopsis: Fem.poly_str()

The result is expressed as a tuple of strings. Of course this will fail on
non-polynomial Fem's."""
        return self.get("poly_str")


    #@GET    FEM:GET('char')
    def char(self):
        """Ouput a (unique) string representation of the Fem.

Synopsis: string = Fem.char()

This can be used to perform comparisons between two different Fem objects."""
        return self.get("char")

    
class Integ:
    """Integration Method Objects.

General object for obtaining handles to various integrations
methods on convexes (used when the elementary matrices are built).
    """
    def __init__(self, *args):
        """Return a FEM Integration Method from a string description.

Integ = Integ(string method)

Here is a list of some integration methods defined in getfem++
(see the description of finite element and integration methods
for a complete reference):

* IM_EXACT_SIMPLEX(n)
  Exact integration on simplices (works only with linear geometric
  transformations and PK Fem's).
* IM_PRODUCT(A,B)
  Product of two integration methods.
* IM_EXACT_PARALLELEPIPED(n)
  Exact integration on parallelepipeds.
* IM_EXACT_PRISM(n)
  Exact integration on prisms.
* IM_GAUSS1D(k)
  Gauss method on the segment, order `k=1,3,...99`.
* IM_NC(n,k)
  Newton-Cotes approximative integration on simplexes, order `k`.
* IM_NC_PARALLELEPIPED(n,k)
  Product of Newton-Cotes integration on parallelepipeds.
* IM_NC_PRISM(n,k)
  Product of Newton-Cotes integration on prisms.
* IM_GAUSS_PARALLELEPIPED(n,k)
  Product of Gauss1D integration on parallelepipeds.
* IM_TRIANGLE(k)
  Gauss methods on triangles `k=1,3,5,6,7,8,9,10,13,17,19`.
* IM_QUAD(k)
  Gauss methods on quadrilaterons `k=2, 3, 5, .. 17`. Note that
  IM_GAUSS_PARALLELEPIPED should be prefered for QK Fem's.
* IM_TETRAHEDRON(k)
  Gauss methods on tetrahedrons `k=1, 2, 3, 5, 6 or 8`.
* IM_SIMPLEX4D(3)
  Gauss method on a 4-dimensional simplex.
* IM_STRUCTURED_COMPOSITE(im,k)
  Composite method on a grid with `k` divisions.
* IM_HCT_COMPOSITE(im)
  Composite integration suited to the HCT composite finite element.

Example:
- Integ('IM_PRODUCT(IM_GAUSS1D(5),IM_GAUSS1D(5))')
is the same as:
- Integ('IM_GAUSS_PARALLELEPIPED(2,5)')

Note that 'exact integration' should be avoided in general, since they
only apply to linear geometric transformations, are quite slow, and
subject to numerical stability problems for high degree Fem's.
        """
        generic_constructor(self,'integ',*args)
    def __del__(self):
        generic_destructor(self,destructible=False)
    def get(self, *args):
        return getfem('integ_get',self.id, *args)
    def __str__(self):
        return self.get('char')
    def __repr__(self):
        return '<getfem.Integ '+str(self)+'>'

    #@RDATTR INTEG:GET('is_exact')
    def is_exact(self):
        """Return 0 if the integration is an approximate one."""
        return self.get("is_exact")


    #@RDATTR INTEG:GET('dim')
    def dim(self):
        """Return the dimension of the reference convex of the method."""
        return self.get("dim")


    #@RDATTR INTEG:GET('nbpts')
    def nbpts(self):
        """Return the total number of integration points.

Synopsis: n = Integ.nbpts()

Count the points for the volume integration, and points for surface
integration on each face of the reference convex.

Only for approximate methods, this has no meaning for exact integration
methods!"""
        return self.get("nbpts")


    #@GET    INTEG:GET('pts')
    def pts(self):
        """Return the list of integration points

Synopsis: Pp = Integ.pts()

Only for approximate methods, this has no meaning for exact integration
methods!"""
        return self.get("pts")


    #@GET    INTEG:GET('face_pts')
    def face_pts(self, F):
        """Return the list of integration points for a face.

Synopsis: Pf = Integ.face_pts(F)

Only for approximate methods, this has no meaning for exact integration
methods!"""
        return self.get("face_pts", F)


    #@GET    INTEG:GET('coeffs')
    def coeffs(self):
        """Returns the coefficients associated to each integration point.

Synopsis: Cp = Integ.coeffs()

Only for approximate methods, this has no meaning for exact integration
methods!"""
        return self.get("coeffs")


    #@GET    INTEG:GET('face_coeffs')
    def face_coeffs(self, F):
        """Returns the coefficients associated to each integration of a face.

Synopsis: Cf = Integ.face_coeffs(F)

Only for approximate methods, this has no meaning for exact integration
methods!"""
        return self.get("face_coeffs", F)


    #@GET    INTEG:GET('char')
    def char(self):
        """Ouput a (unique) string representation of the integration method.

Synopsis: s = Integ.char()

This can be used to comparisons between two different Integ objects."""
        return self.get("char")


class Eltm:
    """Descriptor for an elementary matrix type.

If you have very particular assembling needs, or if you just want to 
check the content of an elementary matrix, this function might be
useful. But the generic assembly abilities of getfem.asm_* should 
suit most needs.
    """
    def __init__(self, *args):
        """Generates a descriptor for an elementary matrix type.

Eltm = Eltm(...)

* Eltm('base', Fem FEM)
  return a descriptor for the integration of shape functions on
  elements, using the Fem `FEM`.
* Eltm('grad', Fem FEM)
  return a descriptor for the integration of the gradient of shape
  functions on elements, using the Fem `FEM`.
* Eltm('hessian', Fem FEM)
  return a descriptor for the integration of the hessian of shape
  functions on elements, using the Fem `FEM`.
* Eltm('normal')
  return a descriptor for the unit normal of convex faces.
* Eltm('grad_geotrans')
  return a descriptor to the gradient matrix of the geometric
  transformation.
* Eltm('grad_geotrans_inv')
  return a descriptor to the inverse of the gradient matrix of the
  geometric transformation (this is rarely used).
* Eltm('product', Eltm A, Eltm B)
  return a descriptor for the integration of the tensorial product
  of elementary matrices `A` and `B`.

In order to obtain a numerical value of theses matrices, see MeshIm.eltm().
        """
        generic_constructor(self,'eltm',*args)
    def __del__(self):
        generic_destructor(self,destructible=False)

    
class CvStruct:
    """Descriptor for a convex structure.

The convex structures are internal structures of getfem++. They do
not contain points positions. These structures are recursive, since
the faces of a convex structures are convex structures.
    """
    def __init__(self, *args):
        generic_constructor(self,'cvstruct',*args)
    def __del__(self):
        generic_destructor(self,destructible=False)
    def get(self, *args):
        return getfem('cvstruct_get',self.id, *args)
    def __repr__(self):
        return '<getfem.CvStruct %dD, %d pts>' % (self.dim(),self.nbpts())

    #@RDATTR CVSTRUCT:GET('nbpts')
    def nbpts(self):
        """Get the number of points of the convex structure."""
        return self.get("nbpts")


    #@RDATTR CVSTRUCT:GET('dim')
    def dim(self):
        """Get the dimension of the convex structure."""
        return self.get("dim")


    #@RDATTR CVSTRUCT:GET('basic structure')
    def basic_structure(self):
        """Get the simplest convex structure.

Synopsis: cs = CvStruct.basic_structure()

For example, the 'basic structure' of the 6-node triangle, is the canonical
3-noded triangle."""
        return self.get("basic structure")


    #@RDATTR CVSTRUCT:GET('face')
    def face(self, F):
        """Return the convex structure of the face `F`."""
        return self.get("face", F)


    #@GET    CVSTRUCT:GET('facepts')
    def facepts(self, F):
        """Return the list of point indices for the face `F`."""
        return self.get("facepts", F)


class Poly:
    pass

class Slice:
    """Mesh slices.

The slices may be considered as a (non-conformal) mesh of simplexes
which provides fast interpolation on a P1-discontinuous MeshFem.
    
It is used mainly for post-processing purposes.
    """
    def __init__(self, *args):
        """General constructor for Slice objects.

- Slice(sliceop, Mesh m, int refine[, mat CVfids])
- Slice(sliceop, MeshFem mf, vec U, int refine[, mat CVfids])
- Slice(sliceop, Slice sl)
- Slice('streamlines', MeshFem mf, vec U, mat SEEDS)
- Slice('points', Mesh m, mat PTS)
- Slice('load', Mesh m)

Creation of a mesh slice. Mesh slices are very similar to a
P1-discontinuous MeshFem on which interpolation is very fast. The
slice is built from a mesh object, and a description of the
slicing operation, for example:

sl = Slice(('planar',+1,array([[0],[0]]),array([[0],[1]])), m, 5);

cuts the original mesh with the half space {y>0}. Each convex of the
original mesh `m` is simplexified (for example a quadrangle is
splitted into 2 triangles), and each simplex is refined 5 times.

Slicing operations can be:

- cutting with a plane, a sphere or a cylinder
- intersection or union of slices
- isovalues surfaces/volumes
- "points", "streamlines" (see below)

If the first argument is a MeshFem mf instead of a mesh, and if it
is followed by a field `U`, then the deformation U will be applied
to the mesh before the slicing operation.

The first argument can also be a slice.

**Slicing operations:**

They are specified with TUPLES, do not forget the extra parentheses!.
The first element is the name of the operation, followed the slicing
options.

* ('none')
  Does not cut the mesh.
* ('planar', int orient, vec p, vec n)
  Planar cut. `p` and `n` define a half-space, `p` being a point belong to
  the boundary of the half-space, and `n` being its normal. If `orient` is
  equal to -1 (resp. 0, +1), then the slicing operation will cut the mesh
  with the "interior" (resp. "boundary", "exterior") of the half-space.
  `orient` may also be set to +2 which means that the mesh will be sliced,
  but both the outer and inner parts will be kept.
* ('ball', int orient, vec c, scalar r)
  Cut with a ball of center `c` and radius `r`.
* ('cylinder', int orient, vec p1, vec p2, scalar r)
  Cut with a cylinder whose axis is the line `(p1,p2)` and whose radius is
  `r`.
* ('isovalues', int orient, MeshFem mf, vec U, scalar V)
  Cut using the isosurface of the field `U` (defined on the MeshFem `mf`).
  The result is the set `{x such that U(x) <= V}` or `{x such that
  U(x)=V}` or `{x such that U(x) >= V}` depending on the value of
  `orient`.
* ('boundary'[, SLICEOP])
  Return the boundary of the result of SLICEOP, where SLICEOP is any
  slicing operation. If SLICEOP is not specified, then the whole mesh is
  considered (i.e. it is equivalent to ('boundary',{'none'})).
* ('explode', mat Coef)
  Build an 'exploded' view of the mesh: each convex is shrinked (0 < Coef
  <= 1). In the case of 3D convexes, only their faces are kept.
* ('union', SLICEOP1, SLICEOP2)
* ('intersection', SLICEOP1, SLICEOP2)
* ('diff', SLICEOP1, SLICEOP2)
* ('comp', SLICEOP)
  Boolean operations: returns the union,intersection, difference or
  complementary of slicing operations.
* ('mesh', Mesh m)
  Build a slice which is the intersection of the sliced mesh with another
  mesh. The slice is such that all of its simplexes are stricly contained
  into a convex of each mesh.

  EXAMPLE:

  sl = Slice(('intersection',('planar',+1,[0;0;0],[0;0;1]),
  ...,('isovalues',-1,mf2,U2,0)),mf,U,5);

**SPECIAL SLICES**:

There are also some special calls to Slice(...)

* Slice('streamlines',MeshFem mf, mat U, mat Seeds)
  compute streamlines of the (vector) field `U`, with seed points given by
  the columns of `Seeds`.
* Slice('points', Mesh m, mat Pts)
  return the "slice" composed of points given by the columns of `Pts`
  (useful for interpolation on a given set of sparse points, see
  compute_interpolate_on(mf, U, sl).
* Slice('load', string filename[, Mesh m])
  load the slice (and its linked mesh if it is not given as an argument)
  from a text file.
        """
        generic_constructor(self,'slice',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def get(self, *args):
        return getfem('slice_get',self.id, *args)
    def set(self, *args):
        return getfem('slice_set',self.id, *args)
    def mesh(self):
        return self.get('linked_mesh')

    #@RDATTR SLICE:GET('dim')
    def dim(self):
        """Return the dimension of the slice (2 for a 2D mesh, etc..)."""
        return self.get("dim")


    #@GET    SLICE:GET('area')
    def area(self):
        """Return the area of the slice."""
        return self.get("area")


    #@GET    SLICE:GET('cvs')
    def cvs(self):
        """Return the list of convexes of the original mesh contained in the slice."""
        return self.get("cvs")


    #@RDATTR SLICE:GET('nbpts')
    def nbpts(self):
        """Return the number of points in the slice."""
        return self.get("nbpts")


    #@RDATTR SLICE:GET('nbsplxs')
    def nbsplxs(self, dim=None):
        """Return the number of simplexes in the slice.

Synopsis: ns = Slice.nbsplxs([, int dim])

Since the slice may contain points (simplexes of dim 0), segments (simplexes
of dimension 1), triangles etc., the result is a vector of size Slice.dim()+1
, except if the optional argument `dim` is used."""
        return self.get("nbsplxs", dim)


    #@GET    SLICE:GET('pts')
    def pts(self):
        """Return the list of point coordinates."""
        return self.get("pts")


    #@GET    SLICE:GET('splxs')
    def splxs(self, dim):
        """Return the list of simplexes of dimension `dim`.

Synopsis: (S, CV2S) = Slice.splxs(int dim)

On output, S has 'dim+1' rows, each column contains the point numbers of a
simplex. The vector `CV2S` can be used to find the list of simplexes for any
convex stored in the slice. For example 'S[:,CV2S[4]:CV2S[5]]' gives the list
of simplexes for the fourth convex."""
        return self.get("splxs", dim)


    #@GET    SLICE:GET('edges')
    def edges(self):
        """Return the edges of the linked mesh contained in the slice.

Synopsis: (P, E1, E2) = Slice.edges()

`P` contains the list of all edge vertices, `E1` contains the indices of each
mesh edge in `P`, and `E2` contains the indices of each "edges" which is on
the border of the slice. This function is useless except for post-processing
purposes."""
        return self.get("edges")


    #@GET    SLICE:GET('interpolate_convex_data')
    def interpolate_convex_data(self, Ucv):
        """Interpolate data given on each convex of the mesh to the slice nodes.

Synopsis: Usl = Slice.interpolate_convex_data(mat Ucv)

The input array `Ucv` may have any number of dimensions, but its last
dimension should be equal to Mesh.max_cvid().

Example of use: Slice.interpolate_convex_data( Mesh.quality())."""
        return self.get("interpolate_convex_data", Ucv)


    #@GET    SLICE:GET('linked mesh')
    def linked_mesh(self):
        """Return the mesh on which the slice was taken."""
        return self.get("linked mesh")


    #@GET    SLICE:GET('memsize')
    def memsize(self):
        """Return the amount of memory (in bytes) used by the slice object."""
        return self.get("memsize")


    #@GET    SLICE:GET('export to vtk')
    def export_to_vtk(self, filename, *args):
        """Export a slice to VTK.

Synopsis: Slice.export_to_vtk(string filename ... [, 'ascii'][, 'edges'] ...)

Following the `filename`, you may use any of the following options:

- if 'ascii' is not used, the file will contain binary data
  (non portable, but fast).
- if 'edges' is used, the edges of the original mesh will be
  written instead of the slice content.

More than one dataset may be written, just list them. Each dataset consists of
either:

- a field interpolated on the slice (scalar, vector or tensor),
  followed by an optional name.
- a mesh_fem and a field, followed by an optional name.

Examples:

- Slice.export_to_vtk('test.vtk',Usl,'first_dataset', mf,
  U2, 'second_dataset')
- Slice.export_to_vtk('test.vtk','ascii',mf,U2)
- Slice.export_to_vtk('test.vtk','edges','ascii',Uslice)"""
        return self.get("export to vtk", filename, *args)


    #@GET    SLICE:GET('export to pov')
    def export_to_pov(self, filename, *args):
        """Export a the triangles of the slice to POV-RAY."""
        return self.get("export to pov", filename, *args)


    #@GET    SLICE:GET('export to dx')
    def export_to_dx(self, filename, *args):
        """Export a slice to OpenDX.

Synopsis: Slice.export_to_dx(string filename, ...)

Following the file name, you may use any of the following options:

- if 'ascii' is not used, the file will contain binary data
  (non portable, but fast).
- if 'edges' is used, the edges of the original mesh will be
  written instead of the slice content.
- if 'append' is used, the opendx file will not be overwritten,
  and the new data will be added at the end of the file.

More than one dataset may be written, just list them. Each dataset consists of
either:

- a field interpolated on the slice (scalar, vector or tensor),
  followed by an optional name.
- a mesh_fem and a field, followed by an optional name."""
        return self.get("export to dx", filename, *args)



    #@SET    SLICE:SET('pts')
    def set_pts(self, P):
        """Replace the points of the slice.

Synopsis: Slice.set_pts(mat P)

The new points `P` are stored in the columns the matrix. Note that you can use
the function to apply a deformation to a slice, or to change the dimension of
the slice (the number of rows of `P` is not required to be equal to
Slice.dim())."""
        return self.set("pts", P)


class Spmat:
    """Getfem sparse matrix."""
    def __init__(self, *args):
        """General constructor for getfem sparse matrices.

These sparse matrix can be stored as CSC (compressed column sparse), which
is the format used by Matlab, or they can be stored as WSC (internal format
to getfem). The CSC matrices are not writable (it would be very inefficient),
but they are optimized for multiplication with vectors, and memory usage.
The WSC are writable, they are very fast with respect to random read/write
operation. However their memory overhead is higher than CSC matrices, and
they are a little bit slower for matrix-vector multiplications.

By default, all newly created matrices are build as WSC matrices. This can
be changed later with Spmat.to_csc(...), or may be changed automatically
by getfem (for example ::LINSOLVE() converts the matrices to CSC).

The matrices may store REAL or COMPLEX values.

* Spmat('empty', int m [, int n])
  Create a new empty (i.e. full of zeros) sparse matrix, of dimensions `m
  x n`. If `n` is omitted, the matrix dimension is `m x m`.
* Spmat('copy', mat K [, list I [, list J]])
  Duplicate a matrix K (which might be a Spmat). If (index) `I` and/or `J`
  are given, the matrix will be a submatrix of `K`. For example:

  M = Spmat('copy', Spmat('empty',50,50), range(40), [6, 7, 8, 3, 10])

  will return a 40x5 matrix.
* Spmat('identity', int n)
  Create a `n x n` identity matrix.
* Spmat('mult', @spmat A, @spmat B)
  Create a sparse matrix as the product of the sparse matrices `A` and
  `B`. It requires that `A` and `B` be both real or both complex, you may
  have to use Spmat.to_complex()
* Spmat('add', @spmat A, @spmat B)
  Create a sparse matrix as the sum of the sparse matrices `A` and `B`.
  Adding a real matrix with a complex matrix is possible.
* Spmat.diag( mat D [, ivec E [, int n [,int m]]])
  Create a diagonal matrix. If `E` is given, `D` might be a matrix and
  each column of `E` will contain the sub-diagonal number that will be
  filled with the corresponding column of `D`.
* Spmat('load','hb'|'harwell-boeing', string filename)
  Read a sparse matrix from an Harwell-Boeing file.
* Spmat('load','mm'|'matrix-market', filename)
  Read a sparse matrix from a Matrix-Market file.
        """
        generic_constructor(self,'spmat',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def __str__(self):
        return self.get('info')
    def __repr__(self):
        return '<getfem.Spmat '+str(self)+'>'
    def __getitem__(self, key):
        return getfem('spmat_get',self.id, 'full',*key)
    def __setitem__(self, key, keyval):
        getfem('spmat_set', self.id, 'assign', key[0], key[1], keyval)
    def __neg__(self):
        m=Spmat('copy',self)
        m.scale(-1)
        return m
    def __add__(self, other):
        return Spmat('add',self,other)
    def __sub__(self, other):
        return Spmat('add',self,other.__neg__())
    def __mul__(self, other):
        """Multiplication of a Spmat with another Spmat or a vector or a scalar.

        The result is another Spmat object.
        """
        if (isinstance(other,(int,float,complex))):
            m = Spmat('copy',self)
            m.set('scale',other)
        elif (isinstance(other,list) or isinstance(other, ndarray)):
            m = self.mult(other)
        else:
            m = Spmat('mult',self,other)
        return m
    def __rmul__(self, other):
        if (isinstance(other,(int,float,complex))):
            m=Spmat('copy',self)
            m.set('scale',other)
        elif (isinstance(other,list) or isinstance(other, ndarray)):
            m=self.tmult(other)
        else:
            m=Spmat('mult',other,self)
        return m;
    def get(self, *args):
        return getfem('spmat_get',self.id, *args)
    def set(self, *args):
        return getfem('spmat_set',self.id, *args)

    #@GET SPMAT:GET('nnz')
    def nnz(self):
        """Return the number of non-null values stored in the sparse matrix."""
        return self.get("nnz")


    #@GET SPMAT:GET('full')
    def full(self, I=None, *args):
        """Return a full (sub-)matrix.

Synopsis: Sm = Spmat.full([, list I[, list J]])

The optional arguments `I` and `J`, are the sub-intervals for the rows and
columns that are to be extracted."""
        return self.get("full", I, *args)


    #@GET SPMAT:GET('mult')
    def mult(self, V):
        """Product of the sparse matrix `M` with a vector `V`.

Synopsis: MV = Spmat.mult(vec V)

For matrix-matrix multiplications, see Spmat('mult')."""
        return self.get("mult", V)


    #@GET SPMAT:GET('tmult')
    def tmult(self, V):
        """Product of `M` transposed (conjugated if `M` is complex) with the vector `V`."""
        return self.get("tmult", V)


    #@GET SPMAT:GET('diag')
    def diag(self, E=None):
        """Return the diagonal of `M` as a vector.

Synopsis: D = Spmat.diag([, list E])

If `E` is used, return the sub-diagonals whose ranks are given in E."""
        return self.get("diag", E)


    #@GET SPMAT:GET('storage')
    def storage(self):
        """Return the storage type currently used for the matrix.

Synopsis: s = Spmat.storage()

The storage is returned as a string, either 'CSC' or 'WSC'."""
        return self.get("storage")


    #@GET SPMAT:GET('size')
    def size(self):
        """Return a vector where `ni` and `nj` are the dimensions of the matrix."""
        return self.get("size")


    #@GET SPMAT:GET('is_complex')
    def is_complex(self):
        """Return 1 if the matrix contains complex values."""
        return self.get("is_complex")


    #@GET SPMAT:GET('csc_ind')
    def csc_ind(self):
        """Return the two usual index arrays of CSC storage.

Synopsis: (JC, IR) = Spmat.csc_ind()

If `M` is not stored as a CSC matrix, it is converted into CSC."""
        return self.get("csc_ind")


    #@GET SPMAT:GET('csc_val')
    def csc_val(self):
        """Return the array of values of all non-zero entries of `M`.

Synopsis: V = Spmat.csc_val()

If `M` is not stored as a CSC matrix, it is converted into CSC."""
        return self.get("csc_val")


    #@GET SPMAT:GET('dirichlet nullspace')
    def dirichlet_nullspace(self, R):
        """Solve the dirichlet conditions `M.U=R`.

Synopsis: (N, U0) = Spmat.dirichlet_nullspace(vec R)

A solution `U0` which has a minimum L2-norm is returned, with a sparse matrix
`N` containing an orthogonal basis of the kernel of the (assembled)
constraints matrix `M` (hence, the PDE linear system should be solved on this
subspace): the initial problem

`K.U = B` with constraints `M.U = R`

is replaced by

`(N'.K.N).UU = N'.B` with `U = N.UU + U0`"""
        return self.get("dirichlet nullspace", R)


    #@GET SPMAT:GET('info')
    def info(self):
        """Return a string contains a short summary on the sparse matrix (dimensions,
filling, ...)."""
        return self.get("info")


    #@GET SPMAT:GET('save')
    def save(self, format, filename):
        """Export the sparse matrix.

Synopsis: Spmat.save(string format, string filename)

the format of the file may be 'hb' for Harwell-Boeing, or 'mm' for Matrix-
Market."""
        return self.get("save", format, filename)



    #@SET SPMAT:SET('clear')
    def clear(self, I=None, *args):
        """Erase the non-zero entries of the matrix.

Synopsis: Spmat.clear([, list I[, list J]])

The optional arguments `I` and `J` may be specified to clear a sub-matrix
instead of the entire matrix."""
        return self.set("clear", I, *args)


    #@SET SPMAT:SET('scale')
    def scale(self, v):
        """Multiplies the matrix by a scalar value `v`."""
        return self.set("scale", v)


    #@SET SPMAT:SET('transpose')
    def transpose(self):
        """Transpose the matrix."""
        return self.set("transpose")


    #@SET SPMAT:SET('conjugate')
    def conjugate(self):
        """Conjugate each element of the matrix."""
        return self.set("conjugate")


    #@SET SPMAT:SET('transconj')
    def transconj(self):
        """Transpose and conjugate the matrix."""
        return self.set("transconj")


    #@SET SPMAT:SET('to_csc')
    def to_csc(self):
        """Convert the matrix to CSC storage.

Synopsis: Spmat.to_csc()

CSC storage is recommended for matrix-vector multiplications."""
        return self.set("to_csc")


    #@SET SPMAT:SET('to_wsc')
    def to_wsc(self):
        """Convert the matrix to WSC storage.

Synopsis: Spmat.to_wsc()

Read and write operation are quite fast with WSC storage."""
        return self.set("to_wsc")


    #@SET SPMAT:SET('to_complex')
    def to_complex(self):
        """Store complex numbers."""
        return self.set("to_complex")


    #@SET SPMAT:SET('diag')
    def diag(self, D, E=None):
        """Change the diagonal (or sub-diagonals) of the matrix.

Synopsis: Spmat.diag(mat D [, ivec E])

If `E` is given, `D` might be a matrix and each column of `E` will contain the
sub-diagonal number that will be filled with the corresponding column of `D`."""
        return self.set("diag", D, E)


    #@SET SPMAT:SET('assign')
    def assign(self, I, J, V):
        """Copy V into the sub-matrix 'M(I,J)'.

Synopsis: Spmat.assign(ivec I, ivec J, mat V)

`V` might be a sparse matrix or a full matrix."""
        return self.set("assign", I, J, V)


    #@SET SPMAT:SET('add')
    def add(self, I, J, V):
        """Add `V` to the sub-matrix 'M(I,J)'.

Synopsis: Spmat.add(ivec I, ivec J, mat V)

`V` might be a sparse matrix or a full matrix."""
        return self.set("add", I, J, V)


    
class Precond:
    """Getfem preconditioner."""
    def __init__(self, *args):
        """General constructor for getfem preconditioners.

The preconditioners may store REAL or COMPLEX values. They accept
getfem sparse matrices and Matlab sparse matrices.

* Precond('identity')
  Create a REAL identity precondioner.
* Precond('cidentity')
  Create a COMPLEX identity precondioner.
* Precond('diagonal', vec D)
  Create a diagonal precondioner.
* Precond('ildlt', Spmat m)
  Create an ILDLT (Cholesky) preconditioner for the (symmetric)
  sparse matrix `m`. This preconditioner has the same sparsity
  pattern than `m` (no fill-in).
* Precond('ilu', Spmat m)
  Create an ILU (Incomplete LU) preconditioner for the sparse
  matrix `m`. This preconditioner has the same sparsity pattern
  than `m` (no fill-in).
* Precond('ildltt', Spmat m[, int fillin[, scalar threshold]])
  Create an ILDLT (Cholesky with filling) preconditioner for the
  (symmetric) sparse matrix `m`. The preconditioner may add at most
  `fillin` additional non-zero entries on each line. The default
  value for `fillin` is 10, and the default threshold is1e-7.
* Precond('ilut', Spmat m[, int fillin[, scalar threshold]])
  Create an ILUT (Incomplete LU with filling) preconditioner for the
  sparse matrix `m`. The preconditioner may add at most `fillin`
  additional non-zero entries on each line. The default value for
  `fillin` is 10, and the default threshold is 1e-7.
* Precond('superlu', Spmat m)
  Uses SuperLU to build an exact factorization of the sparse matrix
  `m`. This preconditioner is only available if the getfem-interface
  was built with SuperLU support. Note that LU factorization is likely
  to eat all your memory for 3D problems. * Precond('spmat', Spmat M)
  ???
        """
        generic_constructor(self,'precond',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def get(self, *args):
        return getfem('precond_get',self.id, *args)
    def set(self, *args):
        return getfem('precond_set',self.id, *args)

    #@GET PRECOND:GET('mult')
    def mult(self, V):
        """Apply the preconditioner to the supplied vector."""
        return self.get("mult", V)


    #@GET PRECOND:GET('tmult')
    def tmult(self, V):
        """Apply the transposed preconditioner to the supplied vector."""
        return self.get("tmult", V)


    #@GET PRECOND:GET('type')
    def type(self):
        """Return a string describing the type of the preconditioner ('ilu', 'ildlt',..)."""
        return self.get("type")


    #@GET PRECOND:GET('size')
    def size(self):
        """Return the dimensions of the preconditioner."""
        return self.get("size")


    #@GET PRECOND:GET('is_complex')
    def is_complex(self):
        """Return 1 if the preconditioner stores complex values."""
        return self.get("is_complex")


    #@GET PRECOND:GET('info')
    def info(self):
        """Return a short informative string about the preconditioner."""
        return self.get("info")





#@FUNC ::LINSOLVE('gmres')
def linsolve_gmres(M, b, restart=None, *args):
    """Solve `M.X = b` with the generalized minimum residuals method.

Synopsis: X = linsolve_gmres(Spmat M, vec b[, int restart][, Precond
P][,'noisy'][,'res', r][,'maxiter', n])

Optionally using `P` as preconditioner. The default value of the restart
parameter is 50."""
    return getfem('linsolve','gmres', M, b, restart, *args)

#@FUNC ::LINSOLVE('cg')
def linsolve_cg(M, b, P=None, *args):
    """Solve `M.X = b` with the conjugated gradient method.

Synopsis: X = linsolve_cg(Spmat M, vec b [, Precond P][,'noisy'][,'res',
r][,'maxiter', n])

Optionally using `P` as preconditioner."""
    return getfem('linsolve','cg', M, b, P, *args)

#@FUNC ::LINSOLVE('bicgstab')
def linsolve_bicgstab(M, b, P=None, *args):
    """Solve `M.X = b` with the bi-conjugated gradient stabilized method.

Synopsis: X = linsolve_bicgstab(Spmat M, vec b [, Precond P][,'noisy'][,'res',
r][,'maxiter', n])

Optionally using `P` as a preconditioner."""
    return getfem('linsolve','bicgstab', M, b, P, *args)

#@FUNC ::LINSOLVE('lu')
def linsolve_lu(M, b):
    """Alias for linsolve_superlu(...)"""
    return getfem('linsolve','lu', M, b)

#@FUNC ::LINSOLVE('superlu')
def linsolve_superlu(M, b):
    """Solve `M.U = b` apply the SuperLU solver (sparse LU factorization).

Synopsis: (U, cond) = linsolve_superlu(Spmat M, vec b)

The condition number estimate `cond` is returned with the solution `U`."""
    return getfem('linsolve','superlu', M, b)


#@FUNC ::COMPUTE('L2 norm')
def compute_L2_norm(mf, U, mim, CVids=None):
    """Compute the L2 norm of the (real or complex) field `U`.

Synopsis: n = compute_L2 norm(mf, U, MeshIm mim[, mat CVids])

If `CVids` is given, the norm will be computed only on the listed convexes."""
    return getfem('compute',mf, U,'L2 norm', mim, CVids)

#@FUNC ::COMPUTE('H1 semi norm')
def compute_H1_semi_norm(mf, U, mim, CVids=None):
    """Compute the L2 norm of grad(`U`).

Synopsis: n = compute_H1 semi norm(mf, U, MeshIm mim[, mat CVids])

If `CVids` is given, the norm will be computed only on the listed convexes."""
    return getfem('compute',mf, U,'H1 semi norm', mim, CVids)

#@FUNC ::COMPUTE('H1 norm')
def compute_H1_norm(mf, U, mim, CVids=None):
    """Compute the H1 norm of `U`.

Synopsis: n = compute_H1 norm(mf, U, MeshIm mim[, mat CVids])

If `CVids` is given, the norm will be computed only on the listed convexes."""
    return getfem('compute',mf, U,'H1 norm', mim, CVids)

#@FUNC ::COMPUTE('H2 semi norm')
def compute_H2_semi_norm(mf, U, mim, CVids=None):
    """Compute the L2 norm of D^2(`U`).

Synopsis: n = compute_H2 semi norm(mf, U, MeshIm mim[, mat CVids])

If `CVids` is given, the norm will be computed only on the listed convexes."""
    return getfem('compute',mf, U,'H2 semi norm', mim, CVids)

#@FUNC ::COMPUTE('H2 norm')
def compute_H2_norm(mf, U, mim, CVids=None):
    """Compute the H2 norm of `U`.

Synopsis: n = compute_H2 norm(mf, U, MeshIm mim[, mat CVids])

If `CVids` is given, the norm will be computed only on the listed convexes."""
    return getfem('compute',mf, U,'H2 norm', mim, CVids)

#@FUNC ::COMPUTE('gradient')
def compute_gradient(mf, U, mf_du):
    """Compute the gradient of the field `U` defined on MeshFem `mf_du`.

Synopsis: DU = compute_gradient(mf, U, MeshFem mf_du)

The gradient is interpolated on the MeshFem `mf_du`, and returned in `DU`. For
example, if `U` is defined on a P2 MeshFem, `DU` should be evaluated on a
P1-discontinuous MeshFem. `mf` and `mf_du` should share the same mesh.

`U` may have any number of dimensions (i.e. this function is not restricted to
the gradient of scalar fields, but may also be used for tensor fields).
However the last dimension of `U` has to be equal to the number of dof of
`mf`. For example, if `U` is a [3x3xNmf] array (where Nmf is the number of dof
of `mf`), `DU` will be a [Nx3x3[xQ]xNmf_du] array, where N is the dimension of
the mesh, Nmf_du is the number of dof of `mf_du`, and the optional Q dimension
is inserted if `Qdim_mf != Qdim_mf_du`, where Qdim_mf is the Qdim of `mf` and
Qdim_mf_du is the Qdim of `mf_du`."""
    return getfem('compute',mf, U,'gradient', mf_du)

#@FUNC ::COMPUTE('hessian')
def compute_hessian(mf, U, mf_h):
    """Compute the hessian of the field `U` defined on MeshFem `mf_h`.

Synopsis: HU = compute_hessian(mf, U, MeshFem mf_h)

See also compute_gradient(mf, U, MeshFem mf_du)."""
    return getfem('compute',mf, U,'hessian', mf_h)

#@FUNC ::COMPUTE('interpolate on')
def compute_interpolate_on(mf, U, *args):
    """Interpolate a field on another MeshFem or a Slice.

Synopsis: Ui = compute_interpolate_on(mf, U, {MeshFem mfi | Slice sli})

- Interpolation on another MeshFem `mfi`:
  `mfi` has to be Lagrangian. If `mf` and `mfi` share the same
  mesh object, the interpolation will be much faster.
- Interpolation on a Slice `sli`:
  this is similar to interpolation on a refined P1-discontinuous
  mesh, but it is much faster. This can also be used with
  Slice('points') to obtain field values at a given set of
  points.

See also asm_interpolation_matrix()"""
    return getfem('compute',mf, U,'interpolate on', *args)

#@FUNC ::COMPUTE('extrapolate on')
def compute_extrapolate_on(mf, U, mfe):
    """Extrapolate a field on another MeshFem.

Synopsis: Ue = compute_extrapolate_on(mf, U, MeshFem mfe)

If the mesh of `mfe` is stricly included in the mesh of `mf`, this function
does stricly the same job as compute_interpolate_on(mf, U, ). However, if the
mesh of `mfe` is not exactly included in `mf` (imagine interpolation between a
curved refined mesh and a coarse mesh), then values which are slightly outside
`mf` will be extrapolated.

See also asm_extrapolation_matrix()"""
    return getfem('compute',mf, U,'extrapolate on', mfe)

#@FUNC ::COMPUTE('error estimate')
def compute_error_estimate(mf, U, mim):
    """Compute an a posteriori error estimation.

Synopsis: E = compute_error_estimate(mf, U, MeshIm mim)

Currently there is only one which is available: for each convex, the jump of
the normal derivative is integrated on its faces."""
    return getfem('compute',mf, U,'error estimate', mim)


#@FUNC ::ASM('mass matrix')
def asm_mass_matrix(mim, mf1, mf2=None):
    """Assembly of a mass matrix.

Synopsis: M = asm_mass_matrix(MeshIm mim, MeshFem mf1[, MeshFem mf2])

Return a Spmat object."""
    return getfem('asm','mass matrix', mim, mf1, mf2)

#@FUNC ::ASM('laplacian')
def asm_laplacian(mim, mf_u, mf_d, a):
    """Assembly of the matrix for the Laplacian problem.

Synopsis: L = asm_laplacian(MeshIm mim, MeshFem mf_u, MeshFem mf_d, vec a)

:math:`\\nabla\\cdot(a(x)\\nabla u)` with `a` scalar.

Return a Spmat object."""
    return getfem('asm','laplacian', mim, mf_u, mf_d, a)

#@FUNC ::ASM('linear elasticity')
def asm_linear_elasticity(mim, mf_u, mf_d, lambda_d, mu_d):
    """Assembles of the matrix for the linear (isotropic) elasticity problem.

Synopsis: Le = asm_linear_elasticity(MeshIm mim, MeshFem mf_u, MeshFem mf_d,
vec lambda_d, vec mu_d)

:math:`\\nabla\\cdot(C(x):\\nabla u)` with :math:`C` defined via `lambda_d`
and `mu_d`.

Return a Spmat object."""
    return getfem('asm','linear elasticity', mim, mf_u, mf_d, lambda_d, mu_d)

#@FUNC ::ASM('nonlinear elasticity')
def asm_nonlinear_elasticity(mim, mf_u, U, law, mf_d, params, *args):
    """Assembles terms (tangent matrix and right hand side) for nonlinear elasticity.

Synopsis: TRHS = asm_nonlinear_elasticity(MeshIm mim, MeshFem mf_u, vec U,
string law, MeshFem mf_d, mat params, {'tangent matrix'|'rhs'|'incompressible
tangent matrix', MeshFem mf_p, vec P|'incompressible rhs', MeshFem mf_p, vec
P})

The solution `U` is required at the current time-step. The `law` may be
choosen among:

- 'SaintVenant Kirchhoff':
  Linearized law, should be avoided). This law has the two usual
  Lame coefficients as parameters, called lambda and mu.
- 'Mooney Rivlin':
  Only for incompressibility. This law has two parameters,
  called C1 and C2.
- 'Ciarlet Geymonat':
  This law has 3 parameters, called lambda, mu and gamma, with
  gamma chosen such that gamma is in ]-lambda/2-mu, -mu[.

The parameters of the material law are described on the MeshFem `mf_d`. The
matrix `params` should have `nbdof(mf_d)` columns, each row correspounds to a
parameter.

The last argument selects what is to be built: either the tangent matrix, or
the right hand side. If the incompressibility is considered, it should be
followed by a MeshFem `mf_p`, for the pression.

Return a Spmat object (tangent matrix), vec object (right hand side), tuple of
Spmat objects (incompressible tangent matrix), or tuple of vec objects
(incompressible right hand side)."""
    return getfem('asm','nonlinear elasticity', mim, mf_u, U, law, mf_d, params, *args)

#@FUNC ::ASM('stokes')
def asm_stokes(mim, mf_u, mf_p, mf_d, nu):
    """Assembly of matrices for the Stokes problem.

Synopsis: (K, B) = asm_stokes(MeshIm mim, MeshFem mf_u, MeshFem mf_p, MeshFem
mf_d, vec nu)

:math:`-\\nu(x)\\Delta u + \\nabla p = 0`

:math:`\\nabla\cdot u = 0`

with :math:`\\nu` (`nu`), the fluid's dynamic viscosity.

On output, `K` is the usual linear elasticity stiffness matrix with
:math:`\\lambda = 0` and :math:`2\\mu = \\nu`. `B` is a matrix corresponding
to :math:`\\int p\\nabla\cdot\\phi`.

`K` and `B` are Spmat object's."""
    return getfem('asm','stokes', mim, mf_u, mf_p, mf_d, nu)

#@FUNC ::ASM('helmholtz')
def asm_helmholtz(mim, mf_u, mf_d, k):
    """Assembly of the matrix for the Helmholtz problem.

Synopsis: A = asm_helmholtz(MeshIm mim, MeshFem mf_u, MeshFem mf_d, vec k)

:math:`\\Delta u + k^2 u` = 0 with `k` complex scalar.

Return a Spmat object."""
    return getfem('asm','helmholtz', mim, mf_u, mf_d, k)

#@FUNC ::ASM('bilaplacian')
def asm_bilaplacian(mim, mf_u, mf_d, a):
    """Assembly of the matrix for the Bilaplacian problem.

Synopsis: A = asm_bilaplacian(MeshIm mim, MeshFem mf_u, MeshFem mf_d, vec a)

:math:`\\Delta(a(x)\\Delta u) = 0` with `a` scalar.

Return a Spmat object."""
    return getfem('asm','bilaplacian', mim, mf_u, mf_d, a)

#@FUNC ::ASM('volumic source')
def asm_volumic_source(mim, mf_u, mf_d, fd):
    """Assembly of a volumic source term.

Synopsis: V = asm_volumic_source(MeshIm mim, MeshFem mf_u, MeshFem mf_d, vec
fd)

Output a vector `V`, assembled on the MeshFem `mf_u`, using the data vector
`fd` defined on the data MeshFem `mf_d`. `fd` may be real or complex-valued.

Return a vec object."""
    return getfem('asm','volumic source', mim, mf_u, mf_d, fd)

#@FUNC ::ASM('boundary source')
def asm_boundary_source(bnum, mim, mf_u, mf_d, G):
    """Assembly of a boundary source term.

Synopsis: B = asm_boundary_source(int bnum, MeshIm mim, MeshFem mf_u, MeshFem
mf_d, vec G)

`G` should be a [Qdim x N] matrix, where N is the number of dof of `mf_d`, and
Qdim is the dimension of the unkown u (that is set when creating the MeshFem).

Return a vec object."""
    return getfem('asm','boundary source', bnum, mim, mf_u, mf_d, G)

#@FUNC ::ASM('dirichlet')
def asm_dirichlet(bnum, mim, mf_u, mf_d, H, R, threshold=None):
    """Assembly of Dirichlet conditions of type `h.u = r`.

Synopsis: (HH, RR) = asm_dirichlet(int bnum, MeshIm mim, MeshFem mf_u, MeshFem
mf_d, mat H, vec R [, threshold])

Handle `h.u = r` where h is a square matrix (of any rank) whose size is equal
to the dimension of the unkown u. This matrix is stored in `H`, one column per
dof in `mf_d`, each column containing the values of the matrix h stored in
fortran order:

`H(:,j) = [h11(x_j) h21(x_j) h12(x_j) h22(x_j)]`

if u is a 2D vector field.

Of course, if the unknown is a scalar field, you just have to set `H = ones(1,
N)`, where N is the number of dof of `mf_d`.

This is basically the same than calling asm_boundary_qu_term() for `H` and
calling asm_neumann() for `R`, except that this function tries to produce a
'better' (more diagonal) constraints matrix (when possible).

See also Spmat.Dirichlet_nullspace()."""
    return getfem('asm','dirichlet', bnum, mim, mf_u, mf_d, H, R, threshold)

#@FUNC ::ASM('boundary qu term')
def asm_boundary_qu_term(boundary_num, mim, mf_u, mf_d, q):
    """Assembly of a boundary qu term.

Synopsis: Q = asm_boundary_qu_term(int boundary_num, MeshIm mim, MeshFem mf_u,
MeshFem mf_d, mat q)

`q` should be be a [Qdim x Qdim x N] array, where N is the number of dof of
`mf_d`, and Qdim is the dimension of the unkown u (that is set when creating
the MeshFem).

Return a Spmat object."""
    return getfem('asm','boundary qu term', boundary_num, mim, mf_u, mf_d, q)

#@FUNC ::ASM('volumic')
def asm_volumic(CVLST=None, *args):
    """Generic assembly procedure for volumic assembly.

Synopsis: (...) = asm_volumic([,CVLST], expr [, mesh_ims, mesh_fems, data...])

The expression `expr` is evaluated over the MeshFem's listed in the arguments
(with optional data) and assigned to the output arguments. For details about
the syntax of assembly expressions, please refer to the getfem user manual (or
look at the file getfem_assembling.h in the getfem++ sources).

For example, the L2 norm of a field can be computed with

compute_L2 norm(mf, U, ) or with:

asm_volumic('u=data(#1);
V()+=u(i).u(j).comp(Base(#1).Base(#1))(i,j)',mim,mf,U)

The Laplacian stiffness matrix can be evaluated with

asm_laplacian(mim, mf, A) or equivalently with:

asm_volumic('a=data(#2);M(#1,#1)+=sym(comp(Grad(#1).Grad(#1).Base(#2))(:,i,:,i
,j).a(j))', mim,mf, A);"""
    return getfem('asm','volumic', CVLST, *args)

#@FUNC ::ASM('boundary')
def asm_boundary(bnum, expr, mim=None, mf=None, data=None, *args):
    """Generic boundary assembly.

Synopsis: (...) = asm_boundary(int bnum, string expr [, MeshIm mim, MeshFem
mf, data...])

See the help for asm_volumic()."""
    return getfem('asm','boundary', bnum, expr, mim, mf, data, *args)

#@FUNC ::ASM('interpolation matrix')
def asm_interpolation_matrix(mf, mfi):
    """Build the interpolation matrix from a MeshFem onto another MeshFem.

Synopsis: Mi = asm_interpolation_matrix(MeshFem mf, MeshFem mfi)

Return a matrix `Mi`, such that `V = Mi.U` is equal to
compute_interpolate_on(mf, U, mfi). Useful for repeated interpolations. Note
that this is just interpolation, no elementary integrations are involved here,
and `mfi` has to be lagrangian. In the more general case, you would have to do
a L2 projection via the mass matrix.

`Mi` is a Spmat object."""
    return getfem('asm','interpolation matrix', mf, mfi)

#@FUNC ::ASM('extrapolation matrix')
def asm_extrapolation_matrix(mf, mfe):
    """Build the extrapolation matrix from a MeshFem onto another MeshFem.

Synopsis: Me = asm_extrapolation_matrix(MeshFem mf, MeshFem mfe)

Return a matrix `Me`, such that `V = Me.U` is equal to
compute_extrapolate_on(mf, U, mfe). Useful for repeated extrapolations. `Me`
is a Spmat object."""
    return getfem('asm','extrapolation matrix', mf, mfe)


#@FUNC ::UTIL('trace level')
def util_trace_level(level):
    """Set the verbosity of some getfem++ routines.

Synopsis: util_('trace_level',int level)

Typically the messages printed by the model bricks, 0 means no trace message
(default is 3)."""
    return getfem('util','trace level', level)

#@FUNC ::UTIL('warning level')
def util_warning_level(level):
    """Filter the less important warnings displayed by getfem.

Synopsis: util_('warning_level',int level)

0 means no warnings, default level is 3."""
    return getfem('util','warning level', level)

class LevelSet:
    """Getfem Level-Set Object.

getfem++ deals with level-set definied by piecewise polynomial
function on a mesh. It will be defined as the zero of this function.
The level-set is represented by a function defined on a lagrange fem
of a certain degree on a mesh.

**IMPORTANT:**

All tools listed below needs the package qhull installed on your
system. This package is widely available. It computes convex hull
and delaunay triangulations in arbitrary dimension. Everything
here is considered *work in progress*, it is still subject to
major changes if needed.
    """
    def __init__(self, *args):
        """General constructor for LevelSet objects.

LevelSet = LevelSet(...)

The level-set object is represented by a primary level-set and optionally
a secondary level-set used to represent fractures (if p(x) is the primary
level-set function and s(x) is the secondary level-set, the crack is defined
by p(x)=0 and s(x)<=0: the role of the secondary is stop the crack).

* LevelSet(Mesh m, int d)
  Create a LevelSet object on a Mesh m represented by a primary function
  defined on a lagrange Fem of degree d.
* LevelSet(Mesh m, int d, string poly1)
  Create a LevelSet object on a Mesh m represented by a primary function
  defined by the polynomial expression poly1 on a lagrange Fem of
  degree d.
* LevelSet(Mesh m, int d, 'with_secondary' [, string poly2])
  Create a LevelSet object on a Mesh m represented by a primary function
  defined by the polynomial expression poly1 and a secondary function,
  both on a lagrange Fem of degree d.
* LevelSet(Mesh m, int d, string poly1, string poly2)
  Create a LevelSet object on a Mesh m represented by a primary function
  defined by the polynomial expression poly1 and a secondary function
  defined by the polynomial expression poly2, both on a lagrange Fem
  of degree d.
        """
        generic_constructor(self,'levelset',*args)
    def __del__(self):
        generic_destructor(self,destructible=True)
    def get(self, *args):
        return getfem('levelset_get',self.id, *args)
    def set(self, *args):
        return getfem('levelset_set',self.id, *args)


    #@GET    LEVELSET:GET('values')
    def values(self, nls):
        """Return the vector of dof for `nls` funtion.

Synopsis: V = LevelSet.values(int nls)

If `nls` is 0, the method return the vector of dof for the primary level-set
funtion. If `nls` is 1, the method return the vector of dof for the secondary
level-set function (if any)."""
        return self.get("values", nls)


    #@RDATTR LEVELSET:GET('degree')
    def degree(self):
        """Return the degree of lagrange representation."""
        return self.get("degree")


    #@GET    LEVELSET:GET('mf')
    def mf(self):
        """Return a reference on the MeshFem object."""
        return self.get("mf")


    #@RDATTR LEVELSET:GET('memsize')
    def memsize(self):
        """Return the amount of memory (in bytes) used by the level-set."""
        return self.get("memsize")



    #@SET    LEVELSET:SET('values')
    def set_values(self, *args):
        """Set values of the vector of dof for the level-set functions.

Synopsis: LevelSet.set_values({mat v1|string poly1}[, {mat v2|string poly2}])

Set the primary function with the vector of dof `v1` (or the polynomial
expression `poly1`) and the secondary function (if any) with the vector of
dof `v2` (or the polynomial expression `poly2`)"""
        return self.set("values", *args)


    #@SET    LEVELSET:SET('simplify')
    def simplify(self, eps=0.01):
        """Simplify dof of level-set optionally with the parameter `eps`."""
        return self.set("simplify", eps)


def memstats():
    print "*** Getfem view of the workspace:"
    getfem('workspace','stats')
    print "*** Python view of the workspace:"
    for id,c in obj_count.iteritems():
        if (c):
            name=str(factory(id).__class__)
            print "%s class %d, id %d : instances=%d" % (name,id.classid,id.objid,c)


def linsolve(what, *args):
    return getfem('linsolve', what, *args)
def compute(mf, U, what, *args):
    return getfem('compute', mf, U, what, *args)
def asm(what, *args):
    return getfem('asm', what, *args)
def util(what, *args):
    return getfem('util', what, *args)


###
#register_types(Mesh,MeshFem,GeoTrans,Fem,Integ,Eltm,CvStruct,Poly,Slice)

def factory(id):
    # must be in the same order than enum getfemint_class_id in gfi_array.h
    t = (Mesh,MeshFem,MeshIm,MdBrick,MdState,Model,GeoTrans,Fem,Integ,Eltm,CvStruct,Poly,Slice,Spmat,Precond,LevelSet)[id.classid]
    return t(id)

register_python_factory(factory)
