Anders Logg wrote: > Where does this show up? >
Not sure what's up. Demo seems to work now. 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: [email protected] >> Reply-To: [email protected] >> To: Garth Wells <[email protected]> >> >> ------------------------------------------------------------ >> revno: 4352 >> committer: Johan Hake <[email protected]> >> 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 ([email protected])" >> -__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(DOLFIN_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 : [email protected] >> Unsubscribe : https://launchpad.net/~dolfin >> More help : https://help.launchpad.net/ListHelp > _______________________________________________ Mailing list: https://launchpad.net/~dolfin Post to : [email protected] Unsubscribe : https://launchpad.net/~dolfin More help : https://help.launchpad.net/ListHelp

