← Back to team overview

dolfin team mailing list archive

Re: [Fwd: [Branch ~dolfin-core/dolfin/main] Rev 4352: Work on Expression in PyDOLFIN]

 

On Monday 30 November 2009 10:03:40 Garth N. Wells wrote:
> Anders Logg wrote:
> > Where does this show up?
> 
> Not sure what's up. Demo seems to work now.

I thought you where aiming at Expressions with an 's'. That was broken until 
it was fixed simultaneously by me and Garth.

Johan

> Garth
> 
> > --
> > Anders
> >
> > On Mon, Nov 30, 2009 at 09:44:24AM +0000, Garth N. Wells wrote:
> >> This change appears to have broken compiled expressions.
> >>
> >> Garth
> >>
> >> -------- Original Message --------
> >> Subject: [Branch ~dolfin-core/dolfin/main] Rev 4352: Work on
> >> Expression in	PyDOLFIN
> >> Date: Mon, 30 Nov 2009 08:11:09 -0000
> >> From: noreply@xxxxxxxxxxxxx
> >> Reply-To: noreply@xxxxxxxxxxxxx
> >> To: Garth Wells <gnw20@xxxxxxxxx>
> >>
> >> ------------------------------------------------------------
> >> revno: 4352
> >> committer: Johan Hake <johan.hake@xxxxxxxxx>
> >> branch nick: dolfin
> >> timestamp: Sun 2009-11-29 23:59:29 -0800
> >> message:
> >>   Work on Expression in PyDOLFIN
> >>     - Simplify expression.py
> >>       * I have split the logic in create_expression_class into on for
> >>         python Expressions and one for compiled ones
> >>       * Removed the possibilty of creating compiled classes that is
> >>         not instantiated. (never used?)
> >>     - Fixes in some typemaps
> >>       * director typemaps for std::vector<double> &values now works
> >>       * I have turned off director for eval(values,x). Could not get it
> >> to work
> >>         It now works through eval(values,data) in python
> >>       * Got argout typemap for std::vector<double> & values to work.
> >>
> >>   Bottom line we are up to par in features wrt Expression in PyDOLFIN
> >>     - Plotting works
> >>     - Subclassing in Python works for both eval and eval_data
> >>     - CompiledExpressions work:
> >>       * MultiLine expressions need to set its value shape if it is
> >>         different from 0
> >>
> >>   TODO:
> >>     - Cleanup in the SWIG interface files and docstrings in
> >> expression.py - get_row, and set_row in GenericMatrix does not work as
> >> intended. Needed to sacrify this one...
> >> modified:
> >>   demo/pde/poisson/python/demo.py
> >>   dolfin/function/Expression.cpp
> >>   dolfin/function/Expression.h
> >>   dolfin/swig/function_post.i
> >>   dolfin/swig/function_pre.i
> >>   dolfin/swig/std_vector_typemaps.i
> >>   site-packages/dolfin/expression.py
> >>
> >>
> >>
> >> === modified file 'demo/pde/poisson/python/demo.py'
> >> --- demo/pde/poisson/python/demo.py	2009-11-29 21:17:18 +0000
> >> +++ demo/pde/poisson/python/demo.py	2009-11-30 07:59:29 +0000
> >> @@ -13,7 +13,7 @@
> >>  """
> >>
> >>  __author__ = "Anders Logg (logg@xxxxxxxxx)"
> >> -__date__ = "2007-08-16 -- 2009-11-24"
> >> +__date__ = "2007-08-16 -- 2009-11-29"
> >>  __copyright__ = "Copyright (C) 2007-2009 Anders Logg"
> >>  __license__  = "GNU LGPL Version 2.1"
> >>
> >> @@ -25,7 +25,7 @@
> >>
> >>  class Source(Expression):
> >>      def eval(self, values, x):
> >> -        values[0] =
> >> 4.0*DOLFIN_PI*DOLFIN_PI*DOLFIN_PI*DOLFIN_PI*sin(DOLFIN_PI*x[0])*sin(DOLF
> >>IN_PI*x[1]) +        values[0] = 10*exp(-((x[0] - 0.5)**2 + (x[1] -
> >> 0.5)**2) / 0.02)
> >>
> >>  # Define Dirichlet boundary (x = 0 or x = 1)
> >>  def boundary(x):
> >> @@ -38,8 +38,8 @@
> >>  # Define variational problem
> >>  v = TestFunction(V)
> >>  u = TrialFunction(V)
> >> -#f = Expression("10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) /
> >> 0.02)") -f = Source(V)
> >> +f = Expression("10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) /
> >> 0.02)") +#f = Source()
> >>  g = Expression("sin(5*x[0])")
> >>  a = inner(grad(v), grad(u))*dx
> >>  L = v*f*dx - v*g*ds
> >> @@ -53,4 +53,4 @@
> >>  file << u
> >>
> >>  # Plot solution
> >> -#plot(u, interactive=True)
> >> +plot(u, interactive=True)
> >>
> >> === modified file 'dolfin/function/Expression.cpp'
> >> --- dolfin/function/Expression.cpp	2009-11-29 12:18:27 +0000
> >> +++ dolfin/function/Expression.cpp	2009-11-30 07:59:29 +0000
> >> @@ -2,7 +2,7 @@
> >>  // Licensed under the GNU LGPL Version 2.1.
> >>  //
> >>  // First added:  2009-09-28
> >> -// Last changed: 2009-10-11
> >> +// Last changed: 2009-11-29
> >>  //
> >>  // Modified by Johan Hake, 2009.
> >>
> >> @@ -114,8 +114,8 @@
> >> 
> >> //----------------------------------------------------------------------
> >>------- void Expression::eval(std::vector<double>& values, const
> >> std::vector<double>& x) const {
> >> -  eval(&values[0], x);
> >> -  //error("Missing eval() for Expression (must be overloaded).");
> >> +  //eval(&values[0], x);
> >> +  error("Missing eval() for Expression (must be overloaded).");
> >>  }
> >> 
> >> //----------------------------------------------------------------------
> >>-------
> >>
> >>
> >> === modified file 'dolfin/function/Expression.h'
> >> --- dolfin/function/Expression.h	2009-11-29 21:17:18 +0000
> >> +++ dolfin/function/Expression.h	2009-11-30 07:59:29 +0000
> >> @@ -2,7 +2,7 @@
> >>  // Licensed under the GNU LGPL Version 2.1.
> >>  //
> >>  // First added:  2009-09-28
> >> -// Last changed: 2009-10-11
> >> +// Last changed: 2009-11-29
> >>
> >>  #ifndef __EXPRESSION_H
> >>  #define __EXPRESSION_H
> >> @@ -75,13 +75,6 @@
> >>      /// Evaluate expression, must be overloaded by user (simple
> >> version) virtual void eval(std::vector<double>& values, const
> >> std::vector<double>& x) const;
> >>
> >> -    // Tempory fix while figuring out SWIG
> >> -    virtual void eval(double* values, const std::vector<double>& x)
> >> const -    {
> >> -      cout << "In eval " << endl;
> >> -      error("Missing eval() for Expression (must be overloaded).");
> >> -    }
> >> -
> >>    protected:
> >>
> >>      // Value shape
> >>
> >> === modified file 'dolfin/swig/function_post.i'
> >> --- dolfin/swig/function_post.i	2009-11-16 15:13:08 +0000
> >> +++ dolfin/swig/function_post.i	2009-11-30 07:59:29 +0000
> >> @@ -25,16 +25,16 @@
> >> 
> >> //----------------------------------------------------------------------
> >>------- // Extend the Data class with an accessor function for the x
> >> coordinates
> >> //----------------------------------------------------------------------
> >>------- -//%extend dolfin::Data {
> >> -//  PyObject* x_() {
> >> -//    npy_intp adims[1];
> >> -//    adims[0] = self->cell().dim();
> >> -//    PyArrayObject* array =
> >> reinterpret_cast<PyArrayObject*>(PyArray_SimpleNewFromData(1, adims,
> >> NPY_DOUBLE, (char *)(self->x))); -//    if ( array == NULL ) return
> >> NULL;
> >> -//    PyArray_INCREF(array);
> >> -//    return reinterpret_cast<PyObject*>(array);
> >> -//  }
> >> -//}
> >> +%extend dolfin::Data {
> >> +  PyObject* x_() {
> >> +    npy_intp adims[1];
> >> +    adims[0] = self->cell().dim();
> >> +    PyArrayObject* array =
> >> reinterpret_cast<PyArrayObject*>(PyArray_SimpleNewFromData(1, adims,
> >> NPY_DOUBLE, reinterpret_cast<char *>( &(const_cast<std::vector<double>&
> >> >(self->x))[0] ))); +    if ( array == NULL ) return NULL;
> >> +    PyArray_INCREF(array);
> >> +    return reinterpret_cast<PyObject*>(array);
> >> +  }
> >> +}
> >>
> >> 
> >> //----------------------------------------------------------------------
> >>------- // Clear director typemaps
> >> @@ -42,3 +42,4 @@
> >>  %clear const double* x;
> >>  %clear double* y;
> >>  %clear const std::vector<double>& x;
> >> +%clear std::vector<double>& values;
> >>
> >> === modified file 'dolfin/swig/function_pre.i'
> >> --- dolfin/swig/function_pre.i	2009-11-29 21:17:18 +0000
> >> +++ dolfin/swig/function_pre.i	2009-11-30 07:59:29 +0000
> >> @@ -9,7 +9,7 @@
> >>  // Modified by Kent-Andre Mardal, 2009
> >>  //
> >>  // First added:  2007-08-16
> >> -// Last changed: 2009-10-07
> >> +// Last changed: 2009-11-29
> >>
> >>  //
> >> ========================================================================
> >>=== // SWIG directives for the DOLFIN function kernel module (pre) @@
> >> -60,8 +60,7 @@
> >> 
> >> //----------------------------------------------------------------------
> >>------- %ignore dolfin::Data::x;
> >>  %rename (x) dolfin::Data::x_();
> >> -%ignore dolfin::Expression::eval(std::vector<double>& values, const
> >> std::vector<double>& x) const; -//%ignore
> >> dolfin::Expression::eval(double* values, const std::vector<double>& x)
> >> const; +//%ignore dolfin::Expression::eval(std::vector<double>& values,
> >> const std::vector<double>& x) const;
> >>
> >> 
> >> //----------------------------------------------------------------------
> >>------- // Modifying the interface of Constant
> >> @@ -118,19 +117,18 @@
> >>  %feature("novaluewrapper") std::vector<double>;
> >>
> >> 
> >> //----------------------------------------------------------------------
> >>------- -// Instantiate a dummy std::vector<dolfin::uint> so value
> >> wrapper is not used +// Instantiate a dummy std::vector<dolfin::double>
> >> so value wrapper is not used
> >> //----------------------------------------------------------------------
> >>------- %template () std::vector<double>;
> >>
> >> 
> >> //----------------------------------------------------------------------
> >>------- -// Typemap for std::vector<dolfin::uint> values
> >> +// Typemap for std::vector<dolfin::double> values (used in Constant
> >> constructor)
> >> //----------------------------------------------------------------------
> >>------- -//%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY) std::vector<double>
> >> values -//{
> >> -//  $1 = PyList_Check($input) ? 1 : 0;
> >> -//}
> >> +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY) std::vector<double> values
> >> +{
> >> +  $1 = PyList_Check($input) ? 1 : 0;
> >> +}
> >>
> >> -/*
> >>  %typemap (in) std::vector<double> values
> >>  {
> >>    if (PyList_Check($input))
> >> @@ -153,7 +151,7 @@
> >>      SWIG_exception(SWIG_TypeError, "expected list of floats");
> >>    }
> >>  }
> >> -*/
> >> +
> >> 
> >> //----------------------------------------------------------------------
> >>------- // Add director classes
> >> 
> >> //----------------------------------------------------------------------
> >>------- @@ -163,28 +161,14 @@
> >>  %feature("nodirector") dolfin::Expression::gather;
> >>  %feature("nodirector") dolfin::Expression::value_dimension;
> >>  %feature("nodirector") dolfin::Expression::value_rank;
> >> +%feature("nodirector") dolfin::Expression::eval(std::vector<double>&
> >> values, const  std::vector<double>& x) const;
> >>
> >> 
> >> //----------------------------------------------------------------------
> >>------- // Director typemap for values in Expression
> >> 
> >> //----------------------------------------------------------------------
> >>------- -%typemap(directorin) double* values
> >> -{
> >> -  {
> >> -    // Compute size of value (number of entries in tensor value)
> >> -    dolfin::uint size = 1;
> >> -    for (dolfin::uint i = 0; i < this->value_rank(); i++)
> >> -      size *= this->value_dimension(i);
> >> -
> >> -    npy_intp dims[1] = {size};
> >> -    $input = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE,
> >> -                                      
> >> reinterpret_cast<char*>($1_name)); -  }
> >> -}
> >> -
> >>  %typemap(directorin) std::vector<double>& values
> >>  {
> >>    {
> >> -    std::cout << "In typemap " << std::endl;
> >>      // Compute size of x
> >>      npy_intp dims[1] = {$1_name.size()};
> >>      $input = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE,
> >> @@ -195,18 +179,11 @@
> >> 
> >> //----------------------------------------------------------------------
> >>------- // Director typemap for coordinates in Expression
> >> 
> >> //----------------------------------------------------------------------
> >>------- -//%typemap(directorin) const double* x {
> >> -//  {
> >> -//    // Compute size of x
> >> -//    npy_intp dims[1] = {this->geometric_dimension()};
> >> -//    $input = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE,
> >> reinterpret_cast<char *>(const_cast<double*>($1_name))); -//  }
> >> -//}
> >> -
> >>  // FIXME: Is there a better way to map a std::vector to a numpy array?
> >>  %typemap(directorin) const std::vector<double>& x
> >>  {
> >>    {
> >> +    std::cout << "In typemap & x" << std::endl;
> >>      // Compute size of x
> >>      npy_intp dims[1] = {$1_name.size()};
> >>      $input = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE,
> >> @@ -214,3 +191,43 @@
> >>    }
> >>  }
> >>
> >> +////-------------------------------------------------------------------
> >>---------- +//// In typemaps for std::vector<double> _array
> >> +////-------------------------------------------------------------------
> >>---------- +//%typemap(in) std::vector<double> & _array
> >> (std::vector<double> vec_tmp) +//{
> >> +//  // Check arguments
> >> +//  if (!PyArray_Check($input)){
> >> +//    PyErr_SetString(PyExc_TypeError, "numpy array of 'double'
> >> expected. Make sure that the numpy array use dtype='d'."); +//    return
> >> NULL;
> >> +//  }
> >> +//
> >> +//  PyArrayObject *xa = reinterpret_cast<PyArrayObject*>(input);
> >> +//  if (!PyArray_TYPE(xa) == NPY_DOUBLE ){
> >> +//    PyErr_SetString(PyExc_TypeError, "numpy array of 'double'
> >> expected. Make sure that the numpy array use dtype='d'."); +//    return
> >> NULL;
> >> +//  }
> >> +//
> >> +//  // Get size and reserve the tmp vector
> >> +//  npy_int size = PyArray_Size(xa);
> >> +//  vec_tmp.reserve(size);
> >> +//
> >> +//  // Get the data
> >> +//  double * data = static_cast<double*>(PyArray_DATA(xa));
> >> +//  for (int i=0, i<size; i++)
> >> +//    vec_tmp[i] = data[i];
> >> +//
> >> +//  // Provide the out argument
> >> +//  $1 = &vec_tmp;
> >> +//}
> >> +//
> >> +//%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY) std::vector<double> & values
> >> +//{
> >> +//    $1 = PyArray_Check($input) ? 1 : 0;
> >> +//}
> >> +//
> >> +//%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY) const std::vector<double> & x
> >> +//{
> >> +//    $1 = PyArray_Check($input) ? 1 : 0;
> >> +//}
> >> +//
> >>
> >> === modified file 'dolfin/swig/std_vector_typemaps.i'
> >> --- dolfin/swig/std_vector_typemaps.i	2009-11-29 21:17:18 +0000
> >> +++ dolfin/swig/std_vector_typemaps.i	2009-11-30 07:59:29 +0000
> >> @@ -3,7 +3,7 @@
> >>  // Licensed under the GNU LGPL Version 2.1.
> >>  //
> >>  // First added:  2009-08-31
> >> -// Last changed: 2009-09-29
> >> +// Last changed: 2009-11-29
> >>
> >> 
> >> //======================================================================
> >>======= // In this file we declare what types that should be able to be
> >> passed using a @@ -132,6 +132,7 @@
> >> 
> >> //----------------------------------------------------------------------
> >>------- %typemap (in,numinputs=0) std::vector<TYPE>& ARG_NAME
> >> (std::vector<TYPE> vec_temp) {
> >> +  // ARGOUT ARG_NAME
> >>    $1 = &vec_temp;
> >>  }
> >>
> >> @@ -184,8 +185,11 @@
> >>  IN_TYPEMAPS_STD_VECTOR_OF_POINTERS(FunctionSpace)
> >>
> >>  ARGOUT_TYPEMAP_STD_VECTOR_OF_PRIMITIVES(dolfin::uint, INT32, cells,
> >> NPY_INT) -ARGOUT_TYPEMAP_STD_VECTOR_OF_PRIMITIVES(dolfin::uint, INT32,
> >> columns, NPY_INT) -ARGOUT_TYPEMAP_STD_VECTOR_OF_PRIMITIVES(double,
> >> DOUBLE, values, NPY_DOUBLE) +// FIXME: We cannot have argout typemaps
> >> for columns and values +// FIXME: They work for get_row, but they
> >> interfere with eval interface. +// FIXME: They should also _not_ kick in
> >> for const std::vector<TYPE>, but they do
> >> +//ARGOUT_TYPEMAP_STD_VECTOR_OF_PRIMITIVES(dolfin::uint, INT32, columns,
> >> NPY_INT) +//ARGOUT_TYPEMAP_STD_VECTOR_OF_PRIMITIVES(double, DOUBLE,
> >> values, NPY_DOUBLE)
> >>
> >> 
> >> //----------------------------------------------------------------------
> >>------- // NumPy to const std::vector<double>& typemap.
> >> @@ -194,28 +198,6 @@
> >>      $1 = PyArray_Check($input) ? 1 : 0;
> >>  }
> >>
> >> -%typemap(in) std::vector<double>& x (std::vector<double> temp)
> >> -{
> >> -  {
> >> -    if (PyArray_Check($input))
> >> -    {
> >> -      PyArrayObject *xa = reinterpret_cast<PyArrayObject*>($input);
> >> -      if ( PyArray_TYPE(xa) == NPY_DOUBLE )
> >> -      {
> >> -        const unsigned int size = PyArray_DIM(xa, 0);
> >> -        temp.resize(size);
> >> -        double* array = static_cast<double*>(PyArray_DATA(xa));
> >> -        std::copy(array, array + size, temp.begin());
> >> -        $1 = &temp;
> >> -      }
> >> -     else
> >> -       SWIG_exception(SWIG_TypeError, "NumPy array expected");
> >> -    }
> >> -    else
> >> -      SWIG_exception(SWIG_TypeError, "NumPy array expected");
> >> -  }
> >> -}
> >> -
> >>  %typemap(in) const std::vector<double>& x (std::vector<double> temp)
> >>  {
> >>    {
> >> @@ -237,19 +219,64 @@
> >>        SWIG_exception(SWIG_TypeError, "NumPy array expected");
> >>    }
> >>  }
> >> -//---------------------------------------------------------------------
> >>-------- -// const std::vector<double>& to NumPy typemap.
> >> -//---------------------------------------------------------------------
> >>-------- -%typemap(argout) const std::vector<double>& x
> >> -{
> >> -  // Do nothing
> >> -}
> >> -//---------------------------------------------------------------------
> >>-------- -// std::vector<double>& to NumPy typemap.
> >> -//---------------------------------------------------------------------
> >>-------- -%typemap(argout) std::vector<double>& x
> >> -{
> >> -  SWIG_exception(SWIG_TypeError, "std::vector<double> to NumPy (argout)
> >> not implemented"); -}
> >> -//---------------------------------------------------------------------
> >>-------- -
> >> +
> >> +//---------------------------------------------------------------------
> >>-------- +// std::vector<double>& to NumPy typemap.
> >> +//---------------------------------------------------------------------
> >>-------- +%typemap(in) std::vector<double>& values (std::vector<double>
> >> temp, PyArrayObject *out_array = 0) +{
> >> +  if (!PyArray_Check($input))
> >> +    SWIG_exception(SWIG_TypeError, "numpy array of 'double' expected.
> >> Make sure that the numpy array use dtype='d'."); +  out_array =
> >> reinterpret_cast<PyArrayObject*>($input);
> >> +  if ( !PyArray_TYPE(out_array) == NPY_DOUBLE )
> >> +    SWIG_exception(SWIG_TypeError, "numpy array of 'double' expected.
> >> Make sure that the numpy array use dtype='d'."); +
> >> +  // Use the size of the incomming array to pass a correct sized vector
> >> to the function +  const unsigned int size = PyArray_DIM(out_array, 0);
> >> +  temp.resize(size);
> >> +  $1 = &temp;
> >> +}
> >> +
> >> +%typemap(argout) std::vector<double>& values
> >> +{
> >> +  // Get the pointer to the data in the passed NumPy array
> >> +  double* array = static_cast<double*>(PyArray_DATA(out_array$argnum));
> >> +
> >> +  // Copy the content from the temp array to the NumPy array
> >> +  std::copy(temp$argnum.begin(), temp$argnum.end(), array);
> >> +
> >> +  // Tell the function to return 'None', which means not return value
> >> +  $result = Py_None;
> >> +
> >> +}
> >> +
> >> +//---------------------------------------------------------------------
> >>-------- +// std::vector<double>& to NumPy typemap.
> >> +//---------------------------------------------------------------------
> >>-------- +%typemap(in) std::vector<double>& vertex_values
> >> (std::vector<double> temp, PyArrayObject *out_array = 0, dolfin::uint
> >> init_size = 0) +{
> >> +  if (!PyArray_Check($input))
> >> +    SWIG_exception(SWIG_TypeError, "numpy array of 'double' expected.
> >> Make sure that the numpy array use dtype='d'."); +  out_array =
> >> reinterpret_cast<PyArrayObject*>($input);
> >> +  if ( !PyArray_TYPE(out_array) == NPY_DOUBLE )
> >> +    SWIG_exception(SWIG_TypeError, "numpy array of 'double' expected.
> >> Make sure that the numpy array use dtype='d'."); +
> >> +  // Use the size of the incomming array to pass a correct sized vector
> >> to the function +  const unsigned int size = PyArray_DIM(out_array, 0);
> >> +  init_size = size;
> >> +  temp.resize(size);
> >> +  $1 = &temp;
> >> +}
> >> +
> >> +%typemap(argout) std::vector<double>& vertex_values
> >> +{
> >> +  // Get the pointer to the data in the passed NumPy array
> >> +  double* array = static_cast<double*>(PyArray_DATA(out_array$argnum));
> >> +
> >> +  // Copy the content from the temp array to the NumPy array
> >> +  std::copy(temp$argnum.begin(), temp$argnum.end(), array);
> >> +
> >> +  // Tell the function to return 'None', which means not return value
> >> +  $result = Py_None;
> >> +
> >> +}
> >>
> >> === modified file 'site-packages/dolfin/expression.py'
> >> --- site-packages/dolfin/expression.py	2009-11-28 21:18:15 +0000
> >> +++ site-packages/dolfin/expression.py	2009-11-30 07:59:29 +0000
> >> @@ -42,7 +42,6 @@
> >>  __license__  = "GNU LGPL Version 2.1"
> >>
> >>  # Modified by Anders Logg, 2008-2009.
> >> -# Modified by Garth N. Wells, 2009.
> >>
> >>  __all__ = ["Expression", "Expressions"]
> >>
> >> @@ -64,96 +63,93 @@
> >>  from compile_expressions import compile_expressions
> >>  from functionspace import *
> >>
> >> -def create_expression_class(name,
> >> -                            cpp_base,
> >> -                            user_bases = None,
> >> -                            user_dict = None,
> >> -                            dim_needs_to_be_passed = False):
> >> +def create_compiled_expression_class(cpp_base):
> >> +    # Check the cpp_base
> >> +    assert(isinstance(cpp_base, (types.ClassType, type)))
> >> +
> >> +    def __init__(self, cpparg, defaults=None, element=None,
> >> degree=None): +        """ Initialize the Expression """
> >> +        # Initialize the cpp base class first and extract value_shape
> >> +        cpp_base.__init__(self)
> >> +        value_shape = tuple(self.value_dimension(i) \
> >> +                                  for i in range(self.value_rank()))
> >> +
> >> +        # Store the dim
> >> +        if len(value_shape) == 0:
> >> +            self._dim = 0
> >> +        elif len(value_shape) == 1:
> >> +            self._dim = value_shape[0]
> >> +        else:
> >> +            self._dim = value_shape
> >> +
> >> +        # Select an appropriate element if not specified
> >> +        if element is None:
> >> +            element = _auto_select_element_from_shape(value_shape,
> >> degree) +        else:
> >> +            # Check that we have an element
> >> +            if not isinstance(element, ufl.FiniteElementBase):
> >> +                raise TypeError, "The 'element' argument must be a UFL
> >> finite element." +
> >> +            # Check same value shape of compiled expression and passed
> >> element +            if not element.value_shape() == value_shape:
> >> +                raise ValueError, "The value shape of the passed
> >> 'element', is not equal to the value shape of the compiled expression."
> >> +
> >> +        # Initialize UFL base class
> >> +        self._ufl_element = element
> >> +        ufl.Function.__init__(self, self._ufl_element)
> >> +
> >> +    # Create and return the class
> >> +    return type("CompiledExpression", (Expression, ufl.Function,
> >> cpp_base), {"__init__":__init__}) +
> >> +def create_python_derived_expression_class(name, user_bases,
> >> user_dict): """Return Expression class
> >>
> >>      This function is used to create all the dynamically created
> >> Expression classes. It takes a name, and a compiled cpp.Expression and
> >> returns -    a dolfin.Expression class. In addition to cpp.Expression
> >> and dolfin.Expression -    it also inherits from ufl.Function.
> >> +    a class that inherits the compiled class together with
> >> dolfin.Expression +    and ufl.Function.
> >>
> >>      @param name:
> >>          The name of the class
> >> -    @param cpp_base:
> >> -        The cpp.Expression base class which the created
> >> -        Expression class shall inherit.
> >>      @param user_bases:
> >> -        Optional user defined bases
> >> +        User defined bases
> >>      @param user_dict:
> >> -        Optional dict with user specified function or attributes
> >> -    @param dim_needs_to_be_passed:
> >> -        Optional if a simple expression using cpparg is created with no
> >> -        information about geometric dimensions
> >> +        Dict with user specified function or attributes
> >>      """
> >>
> >> -    # Check the name
> >> +    # Check args
> >>      assert(isinstance(name, str))
> >> -    assert(name != "Expression"), "Cannot create a sub class of
> >> Expression with the same name as Expression" -
> >> -    assert(isinstance(cpp_base, (types.ClassType, type)))
> >> +    assert(isinstance(user_bases, list))
> >> +    assert(isinstance(user_dict, dict))
> >>
> >>      # Define the bases
> >> -    user_bases = user_bases or []
> >>      assert(all([isinstance(t, (types.ClassType, type)) for t in
> >> user_bases])) -    bases = tuple([Expression, ufl.Function, cpp_base] +
> >> user_bases) -
> >> -    # Define the dictionary of the class
> >> -    dict_ = user_dict or {}
> >> -    assert isinstance(dict_, dict)
> >> -
> >> +    bases = tuple([Expression, ufl.Function, cpp.Expression] +
> >> user_bases) +
> >>      # If a user init is not provided create a dummy one
> >> -    if "__init__" not in dict_:
> >> +    if "__init__" not in user_dict:
> >>          def user_init(self, *arg, **kwargs): pass
> >>      else:
> >> -        user_init = dict_.pop("__init__")
> >> -
> >> -    # If a user init is not provided create a dummy one
> >> -    if "dim" not in dict_:
> >> -        def user_dim(self): return 1
> >> -    else:
> >> -        user_dim = dict_.pop("dim")
> >> +        user_init = user_dict.pop("__init__")
> >>
> >>      def __init__(self, *args, **kwargs):
> >> -        # This is called if no user defined init function is provided
> >>
> >>          # Get element and degree
> >> -        element = kwargs.pop("element", None)
> >> -        degree = kwargs.pop("degree", None)
> >> +        element = kwargs.get("element", None)
> >> +        degree = kwargs.get("degree", None)
> >>
> >>          # Select an appropriate element if not specified
> >> -        if element is None and len(args) > 0:
> >> -            element = _auto_select_element_from_cpparg(args[0], degree)
> >> -        elif element is None:
> >> -            element = _auto_select_element_from_dim(user_dim(self),
> >> degree) -
> >> -        # Check that we have have an element
> >> -        if not isinstance(element, ufl.FiniteElementBase):
> >> +        if element is None:
> >> +            element = _auto_select_element_from_dim(self.dim(), degree)
> >> +        elif not isinstance(element, ufl.FiniteElementBase):
> >>              raise TypeError, "The 'element' argument must be a UFL
> >> finite element." -
> >> +
> >>          # Initialize UFL base class
> >>          self._ufl_element = element
> >>          ufl.Function.__init__(self, self._ufl_element)
> >>
> >>          # Initialize cpp_base class
> >> -
> >> -        # First check if we are instantiating a user-defined Python
> >> class -        if "eval" in dict_ or "eval_data" in dict_:
> >> -            assert cpp_base == cpp.Expression
> >> -            cpp_base.__init__(self,
> >> list(self._ufl_element.value_shape())) -        else:
> >> -            cpp_base.__init__(self)
> >> -
> >> -        # Check that the value_shape of the ufl.FiniteElement
> >> corresponds with the -        # created cpp.Expression
> >> -        shape = self._ufl_element.value_shape()
> >> -        if not (self.value_rank() == len(shape) and
> >> -           all(dim == self.value_dimension(i) for i, dim in
> >> enumerate(shape))): -            exp_shape =
> >> tuple(self.value_dimension(i) for i in xrange(self.value_rank())) -     
> >>       raise ValueError, "value_shape of passed element does not match
> >> value_shape of the Expression: %s != %s"%(str(shape), str(exp_shape)) + 
> >>       cpp.Expression.__init__(self,
> >> list(self._ufl_element.value_shape()))
> >>
> >>          # Calling the user defined_init
> >>          user_init(self, *args, **kwargs)
> >> @@ -165,6 +161,7 @@
> >>          __init__.__doc__ = """ Initialize the Expression"""
> >>
> >>      # NOTE: Do not prevent the user to overload attributes "reserved"
> >> by PyDOLFIN +    # NOTE: Why not?
> >>
> >>      ## Collect reserved attributes from both cpp.Function and
> >> ufl.Function #reserved_attr = dir(ufl.Function)
> >> @@ -180,22 +177,22 @@
> >>      #    if attr in dict_:
> >>      #        raise TypeError, "The Function attribute '%s' is reserved
> >> by PyDOLFIN."%attr
> >>
> >> -    # Fill the dict_ with constructed function
> >> -    dict_["__init__"]  = __init__
> >> +    # Add __init__ to the user_dict
> >> +    user_dict["__init__"]  = __init__
> >>
> >>      # Create the class and return it
> >> -    return type(name, bases, dict_)
> >> +    return type(name, bases, user_dict)
> >>
> >>  class ExpressionMetaClass(type):
> >> -
> >> +    """ Meta Class for Expression"""
> >>      def __new__(cls, name, bases, dict_):
> >> -        """ Return a new Expression class """
> >> +        """ Returns a new Expression class """
> >>
> >>          assert(isinstance(name, str)), "Expecting a 'str'"
> >>          assert(isinstance(bases, tuple)), "Expecting a 'tuple'"
> >>          assert(isinstance(dict_, dict)), "Expecting a 'dict'"
> >>
> >> -        # First check if we are creating the Function class
> >> +        # First check if we are creating the Expression class
> >>          if name == "Expression":
> >>              # Assert that the class is _not_ a subclass of Expression,
> >>              # i.e., a user have tried to:
> >> @@ -209,7 +206,9 @@
> >>              # this module
> >>              return type.__new__(cls, name, bases, dict_)
> >>
> >> -        # If subclassing Expression directly (used in
> >> specialfunctions.py) +        # If creating a fullfledged derived
> >> expression class, i.e, inheriting +        # dolfin.Expression,
> >> ufl.Function and cpp.Expression (or a subclass) +        # then just
> >> return the new class.
> >>          if len(bases) >= 3 and bases[0] == Expression and \
> >>                 bases[1] == ufl.Function and issubclass(bases[2],
> >> cpp.Expression): # Return the instantiated class
> >> @@ -221,59 +220,28 @@
> >>          # remove Expression, to be added later
> >>          user_bases.remove(Expression)
> >>
> >> -        # Check the cppcode and eval attributes
> >> -        if 'cpparg' in dict_  and ('eval' in dict_ or 'eval_data' in
> >> dict_) : -            raise TypeError, "Cannot create class with both
> >> 'eval'/'eval_data' and 'cpparg' attributes defined." -
> >> -        # If the Expression class is a user defined python class, case
> >> 4. from docstring -        if 'eval' in dict_ or 'eval_data' in dict_:
> >> -            # Get name of eval function
> >> -            eval_name = 'eval' if 'eval' in dict_ else 'eval_data'
> >> -
> >> -            user_eval = dict_[eval_name]
> >> -
> >> -            # Check type and number of arguments of user_eval function
> >> -            if not isinstance(user_eval, types.FunctionType):
> >> -                raise TypeError, "'%s' attribute must be a
> >> 'function'"%eval_name -            if not
> >> user_eval.func_code.co_argcount == 3:
> >> -                raise TypeError, "The overloaded '%s' function must use
> >> three arguments"%eval_name -
> >> -            return create_expression_class(name, cpp.Expression,
> >> user_bases, dict_) -
> >> -        # If cpparg is provided, case 5. from docstring
> >> -        if 'cpparg' in dict_:
> >> -
> >> -            # Check the handed attributes and return an args tuple
> >> -            cpparg   = dict_.pop('cpparg')
> >> -            defaults = dict_.pop("defaults",None)
> >> -
> >> -            # Check that the user has not provide any other attributes
> >> -            # than the allowed ones.
> >> -            if len(dict_) > 1:
> >> -                dict_.pop('__module__')
> >> -                raise TypeError, "Not allowed to provide user defined
> >> attributes to a sub class of Expression when the compiled function
> >> interface is used. Found: %s"%\ -                      (",
> >> ".join(["'%s'"%key for key in dict_.iterkeys()])) -
> >> -            # Check arguments
> >> -            _check_cpparg(cpparg)
> >> -            _check_defaults(defaults)
> >> -
> >> -            # Compile the cppargs
> >> -            cpp_base = compile_expressions([cpparg], [defaults])[0]
> >> -
> >> -            # Add back the cpparg as an attribute
> >> -            cpp_base.cpparg = cpparg
> >> -
> >> -            # If defaults where handed add it back too
> >> -            if defaults is not None:
> >> -                cpp_base.defaults = defaults
> >> -
> >> -            # Create the Expression class and return it
> >> -            return create_expression_class(name, cpp_base, user_bases,
> >> -                                           dim_needs_to_be_passed = not
> >> _is_complex_expression(cpparg)) -
> >> -        # If we have reached this stage raise error
> >> -        raise TypeError, "Error in subclassing Expression. For correct
> >> usage see 4. and 5. in Expression docstring." +        # Check the user
> >> has provided either an eval or eval_data method +        if not ('eval'
> >> in dict_ or 'eval_data' in dict_):
> >> +            raise TypeError, "expected an overload 'eval' or
> >> 'eval_data' method" +
> >> +        # Get name of eval function
> >> +        eval_name = 'eval' if 'eval' in dict_ else 'eval_data'
> >> +
> >> +        user_eval = dict_[eval_name]
> >> +
> >> +        # Check type and number of arguments of user_eval function
> >> +        if not isinstance(user_eval, types.FunctionType):
> >> +            raise TypeError, "'%s' attribute must be a
> >> 'function'"%eval_name +        if not user_eval.func_code.co_argcount ==
> >> 3:
> >> +            raise TypeError, "The overloaded '%s' function must use
> >> three arguments"%eval_name +        # A nice hack to get around some
> >> SWIG director problems +        # In short: eval_data works but not
> >> eval...
> >> +        if eval_name == 'eval':
> >> +            def eval_data(self, values, data):
> >> +                user_eval(self, values, data.x())
> >> +            dict_['eval_data'] = eval_data
> >> +
> >> +        return create_python_derived_expression_class(name, user_bases,
> >> dict_)
> >>
> >>  #--- The user interface ---
> >>
> >> @@ -324,7 +292,6 @@
> >>          return ufl.Function.__call__(self,*args)
> >>
> >>      # Some help variables
> >> -    #dim = self.geometric_dimension()
> >>      value_size = ufl.common.product(self.ufl_element().value_shape())
> >>
> >>      # If values (return argument) is passed, check the type and length
> >> @@ -349,19 +316,14 @@
> >>              raise TypeError, "expected a scalar or an iterable as
> >> coordinate argument" # Check for scalar x
> >>          if isinstance(x[0], (int, float)):
> >> -            #if not dim == 1:
> >> -            #    raise TypeError, "expected a coordinate argument of
> >> length %d"%dim x = numpy.fromiter(x, 'd')
> >>          else:
> >>              x = x[0]
> >> -            #if len(x) != dim:
> >> -            #    raise TypeError, "expected an iterable of length %d as
> >> coordinate argument"%dim if isinstance(x, (list, tuple)):
> >>                  x = numpy.fromiter(x, 'd')
> >>
> >>      # If several x arguments have been provided
> >>      else:
> >> -        #if len(x) != dim or not all(isinstance(v,(int,float)) for v in
> >> x): if not all(isinstance(v,(int,float)) for v in x):
> >>              raise TypeError, "expected different number of scalar
> >> arguments for the coordinates" x = numpy.fromiter(x,'d')
> >> @@ -550,8 +512,8 @@
> >>            Optional quadrature degree element.
> >>          """
> >>
> >> -        # If the __new__ function is called because we are
> >> instantiating a sub -        # class of Expression, then use the
> >> object's __new__ function instead +        # If the __new__ function is
> >> called because we are instantiating a python sub +        # class of
> >> Expression, then just return a new instant of the passed class if
> >> cls.__name__ != "Expression":
> >>              return object.__new__(cls)
> >>
> >> @@ -562,85 +524,111 @@
> >>          # Compile module and get the cpp.Expression class
> >>          cpp_base = compile_expressions([cpparg], [defaults])[0]
> >>
> >> -        # Store arguments for later use
> >> +        # Store compile arguments for later use
> >>          cpp_base.cpparg = cpparg
> >>          cpp_base.defaults = defaults
> >> -        cpp_base.element = element
> >> -        cpp_base.degree = degree
> >> -        return
> >> object.__new__(create_expression_class("CompiledExpression", cpp_base))
> >> -
> >> +
> >> +        return
> >> object.__new__(create_compiled_expression_class(cpp_base)) +
> >> +    # This method is only included so a user can check what arguments
> >> +    # one should use in IPython using tab completion
> >> +    def __init__(self, cpparg=None, defaults=None, element=None,
> >> degree=None):pass +
> >> +    # Reuse the docstring from __new__
> >> +    __init__.__doc__ = __new__.__doc__
> >> +
> >>      def ufl_element(self):
> >>          " Return the ufl FiniteElement."
> >>          return self._ufl_element
> >>
> >>      def __str__(self):
> >> +        "x.__str__() <==> print(x)"
> >>          # FIXME: We might change this using rank and dimension instead
> >>          return "<Expression on a %s>" % str(self._ufl_element)
> >>
> >>      def __repr__(self):
> >> +        "x.__repr__() <==> repr(x)"
> >>          return ufl.Function.__repr__(self)
> >>
> >> +    # Default value of dim
> >> +    _dim = 0
> >> +
> >> +    def dim(self):
> >> +        """ Returns the dimension of the value"""
> >> +        return self._dim
> >> +
> >> +    __call__ = expression__call__
> >> +
> >>  def Expressions(*args, **kwargs):
> >>      """ Batch-processed user-defined JIT-compiled expressions
> >>      -------------------------------------------------------
> >>
> >>      By specifying several cppargs one may compile more than one
> >> expression -    at a time. These may either be instantiated using a
> >> single FunctionSpace -    common for all expressions, using the optional
> >> kwarg 'V', or with -    a separate FunctionSpace for each cpparg:
> >>
> >> +    at a time:
> >>      >>> f0, f1 = Expressions('sin(x[0]) + cos(x[1])', 'exp(x[1])',
> >>      >>> degree=3)
> >>
> >> -    >>> f0, f1 = Expressions('sin(x[0]) + cos(x[1])', 'exp(x[1])',
> >> element=element) -
> >> -    Here cppcode is a code snippet, which should be a string of C++
> >> +
> >> +    >>> f0, f1, f2 = Expressions((('A*sin(x[0])', 'B*cos(x[1])')
> >> +                                 ('0','1')), {'A':2.0,'B':3.0},
> >> +                                 code,
> >> +                                 (('cos(x[0])','sin(x[1])'),
> >> +                                 ('sin(x[0])','cos(x[1])')),
> >> element=element) +
> >> +    Here code is a C++ code snippet, which should be a string of C++
> >>      code that implements a class that inherits from dolfin::Expression,
> >>      see user case 3. in Expression docstring
> >>
> >>      Batch-processing of JIT-compiled expressions may significantly
> >> speed up JIT-compilation at run-time.
> >> -
> >> -"""
> >> +    """
> >>
> >>      # Get the element/degree from kwarg
> >> +    if len(kwargs) > 1:
> >> +        raise TypeError, "Can only define one kwarg and that can only
> >> be 'degree' or 'element'." degree  = kwargs.pop("degree", None)
> >>      element = kwargs.pop("element", None)
> >> -    if len(kwargs) > 1:
> >> -        raise TypeError, "Can only define one kwarg and that can only
> >> be 'degree' or 'element'."
> >>
> >>      # Iterate over the *args and collect input to compile_expressions
> >>      cppargs = []; defaults = []; i = 0;
> >> -    while i < len(args):
> >> +    while i < nargs:
> >> +
> >> +        # Check type of cppargs
> >>          if not isinstance(args[i],(tuple, list, str)):
> >>              raise TypeError, "Expected either a 'list', 'tuple' or
> >> 'str' for argument %d"%i +
> >>          cppargs.append(args[i])
> >> -        defaults.append(None)
> >>          i += 1
> >>
> >> +        # If we have more args and the next is a dict
> >> +        if i < nargs and isinstance(args[i], dict):
> >> +            # Append the dict to defaults
> >> +            _check_defaults(args[i])
> >> +            defaults.append(args[i])
> >> +            i += 1
> >> +        else:
> >> +            # If not append None
> >> +            defaults.append(None)
> >> +
> >>      # Compile the cpp.Expressions
> >>      cpp_bases = compile_expressions(cppargs, defaults)
> >>
> >>      # Instantiate the return arguments
> >>      return_expressions = []
> >> -    for i, cpp_base in enumerate(cpp_bases):
> >> -       
> >> return_expressions.append(create_expression_class("CompiledExpression",
> >> cpp_base) -                                 (cppargs[i],
> >> element=element, degree=degree)) +
> >> +    for cpp_base in cpp_bases:
> >> +        # If we only want the cpp.Expression
> >> +       
> >> return_expressions.append(create_compiled_expression_class(cpp_base)(\ +
> >>            degree=degree,
> >> +            element=element))
> >>
> >>      # Return the instantiated Expressions
> >>      return tuple(return_expressions)
> >>
> >> -# Assign doc string
> >> -expression__call__.__doc__
> >> -
> >> -# Assign __call__ method
> >> -Expression.__call__ = types.MethodType(expression__call__, None,
> >> Expression) -
> >>  #--- Utility functions ---
> >>
> >>  def _check_cpparg(cpparg):
> >>      "Check that cpparg makes sense"
> >>
> >> -    if cpparg is None: return
> >> -
> >>      # Check that we get a string expression or nested expression
> >>      if not isinstance(cpparg, (str, tuple, list)):
> >>          raise TypeError, "Please provide a 'str', 'tuple' or 'list' for
> >> the 'cpparg' argument." @@ -665,14 +653,6 @@
> >>      "Check if cpparg is a complex expression"
> >>      return isinstance(cpparg, str) and "class" in cpparg and
> >> "Expression" in cpparg
> >>
> >> -def _auto_select_element_from_cpparg(cpparg, degree=None):
> >> -    "Automatically select an appropriate element from cpparg."
> >> -
> >> -    # Use numpy to get the shape
> >> -    shape = numpy.shape(cpparg)
> >> -
> >> -    return _auto_select_element_from_shape(shape, degree)
> >> -
> >>  def _auto_select_element_from_dim(dim, degree=None):
> >>      "Automatically select an appropriate element from dim."
> >>
> >> @@ -708,3 +688,12 @@
> >>      cpp.info("Automatic selection of expression element: " +
> >> str(element))
> >>
> >>      return element
> >> +
> >> +def _check_name_and_base(name, cpp_base):
> >> +    # Check the name
> >> +    assert(isinstance(name, str))
> >> +    assert(name != "Expression"), "Cannot create a sub class of
> >> Expression with the same name as Expression" +
> >> +    assert(isinstance(cpp_base, (types.ClassType, type)))
> >> +
> >> +
> >>
> >>
> >>
> >> _______________________________________________
> >> Mailing list: https://launchpad.net/~dolfin
> >> Post to     : dolfin@xxxxxxxxxxxxxxxxxxx
> >> Unsubscribe : https://launchpad.net/~dolfin
> >> More help   : https://help.launchpad.net/ListHelp
> 
> _______________________________________________
> Mailing list: https://launchpad.net/~dolfin
> Post to     : dolfin@xxxxxxxxxxxxxxxxxxx
> Unsubscribe : https://launchpad.net/~dolfin
> More help   : https://help.launchpad.net/ListHelp
> 



References