Customized Expression class example does not work

Issue #761 resolved
Simon Funke created an issue

I tried to run the customized Expression example in the dolfin documentation (last code example in https://fenicsproject.org/documentation/dolfin/dev/python/programmers-reference/functions/expression/Expression.html)

A slightly simplified version of this script is:

from fenics import *
mesh = UnitIntervalMesh(10)

class MyExpression1(Expression):
    def __init__(self, mesh):
        self._mesh = mesh
    def eval(self, values, x):
        pass

MyExpression1(degree=0, mesh=mesh)

However, when running this example on dolfin master, one gets:

git:master$ python test.py 
Traceback (most recent call last):
  File "test.py", line 10, in <module>
    MyExpression1(degree=0, mesh=mesh)
  File "/home/simon/src/fenics_master/dolfin_master/local.master/lib/python2.7/site-packages/dolfin/functions/expression.py", line 282, in __init__
    user_init(self, *args, **kwargs)
TypeError: __init__() got an unexpected keyword argument 'degree'
Aborted (core dumped)

Comments (10)

  1. Miklós Homolya

    The __init__ method of your MyExpression1 class clearly does not expect a degree argument. Did you mean to write

        def __init__(self, mesh, **kwargs):
            super(MyExpression1, self).__init__(**kwargs)
            self._mesh = mesh
    

    and then MyExpression1(mesh=mesh, degree=0)?

    Edit: ah, you probably mean that the documentation is wrong...

  2. Simon Funke reporter

    Yes, I was referring to the documentation. In other words, the documentation should be updated to:

    from fenics import *
    mesh = UnitIntervalMesh(10)
    
    class MyExpression1(Expression):
        def __init__(self, **kwargs):
            self._mesh = kwargs["mesh"]
        def eval(self, values, x):
            pass
    
    MyExpression1(degree=0, mesh=mesh)
    

    Ill make a pull request

  3. Miklós Homolya

    Well, your proposed alternative might work, but is super misleading. kwargs is never used, so the parameters degree=0 and mesh=mesh are thrown away, never used, and the constructor of the super class is never called. self._mesh = mesh will still work though, because it picks up the global variable mesh = UnitIntervalMesh(10).

  4. Jan Blechta

    Not sure whether it changed recently, but Expression.__init__(self, *foo, **bar) did not use to work from Expression subclass. (I made it an error to avoid confusion.) There is (or at least was) a lot of metaclass magic and utilization of new and unused kwargs in Simon's example might be actually used there.

  5. Mikael Mortensen

    That's actually not true. But there's some magic to it, and you cannot call the base class__init__ because of this magic. See documentation of Expression:

    The user can customize initialization of derived Expressions.
    However, because of magic behind the scenes, a user needs to
    pass optional arguments to __init__ using ``**kwargs``, and
    _not_ calling the base class __init__:
    

    If you check out m=MyExpression1(degree=0, mesh=mesh) and m.element you will see that it's MyExpression1.element of Coefficient(FunctionSpace(None, FiniteElement('Discontinuous Lagrange', None, 0)), 2). Try sending in degree=2.

  6. Jan Blechta

    On the other hand I agree that the current state is confusing. But there does not seem to be an obvious fix.

  7. Jan Blechta

    The other issue is that failing MWE from the description fails with TypeError: init() got an unexpected keyword argument 'degree' rather than expression without element or degree are illega`.

    EDIT: Sorry, I can't read.

  8. Log in to comment