How do dolfin Parameters play with pybind11?

Hello,

I am having some trouble figuring out how dolfin’s parameters play with the new pybind11 method for wrapping C++ functions into python code. In the MWE below I try following the example at dolfin::Parameters Class Reference

Running FEniCS v2019.1.0 via Docker on a Mac OS 10.15.2, this fails with the following error message:

###############

fenics@e2603a6268f7:~/shared/sandbox/pybind$ python3 parameterTest_pybind11.py 
------------------- Start compiler output ------------------------
/tmp/tmpjoagxulm/dolfin_cpp_module_2183c0f3006d21321ed4c9ea0b74e338.cpp:27:33: error: expected identifier before string constant
   dolfin::Parameters parameters("my_parameters");
                                 ^
/tmp/tmpjoagxulm/dolfin_cpp_module_2183c0f3006d21321ed4c9ea0b74e338.cpp:27:33: error: expected ‘,’ or ‘...’ before string constant
/tmp/tmpjoagxulm/dolfin_cpp_module_2183c0f3006d21321ed4c9ea0b74e338.cpp:28:3: error: ‘parameters’ does not name a type
   parameters.add("par", 1.0);
   ^

-------------------  End compiler output  ------------------------
Compilation failed! Sources, command, and errors have been written to: /home/fenics/shared/sandbox/pybind/jitfailure-dolfin_cpp_module_2183c0f3006d21321ed4c9ea0b74e338
Traceback (most recent call last):
  File "parameterTest_pybind11.py", line 48, in <module>
    profile = compile_cpp_code(cpp_code)
  File "/usr/local/lib/python3.5/dist-packages/dolfin/jit/pybind11jit.py", line 87, in compile_cpp_code
    generate=jit_generate)
  File "/usr/local/lib/python3.5/dist-packages/dolfin/jit/jit.py", line 47, in mpi_jit
    return local_jit(*args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/dolfin/jit/jit.py", line 103, in dijitso_jit
    return dijitso.jit(*args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/dijitso/jit.py", line 217, in jit
    % err_info['fail_dir'], err_info)
dijitso.jit.DijitsoError: Dijitso JIT compilation failed, see '/home/fenics/shared/sandbox/pybind/jitfailure-dolfin_cpp_module_2183c0f3006d21321ed4c9ea0b74e338' for details
fenics@e2603a6268f7:~/shared/sandbox/pybind$ 


###############

Following the pybind11 documentation I was able to implement private variables with field-like python interface. Is this approach recommended over the Parameters module now?

Thanks for any suggestions.

MWE:

from dolfin import *

cpp_code = """
#include <pybind11/pybind11.h>
namespace py = pybind11;

#include <dolfin/function/Expression.h>
#include <dolfin/parameter/dolfin_parameter.h>

class Profile : public dolfin::Expression
{
public:

  Profile() : dolfin::Expression(){}

  // Set variable
  void set_p(const double &pval_)
  {
  pval = pval_;
  }

  // Get variable
  const double get_p() const
  {
  return pval;
  }

  // Attempt to add parameters
  dolfin::Parameters parameters("my_parameters");
  parameters.add("par", 1.0);

private:

  // private variable
  double pval = 0.0;

};

PYBIND11_MODULE(SIGNATURE, m)
{
  py::class_<Profile, std::shared_ptr<Profile>, dolfin::Expression>
    (m, "Profile")
    .def(py::init<>())
    .def_property("pval", &Profile::get_p, &Profile::set_p); // expects set and get functions
}
"""
# Compile expression
profile = compile_cpp_code(cpp_code)
expr = CompiledExpression(profile.Profile(), degree=1)

# Test access to private variable
print(expr.pval)
expr.pval = 5.0
print(expr.pval)

# Does similar access apply to fenics parameters?

Hello,

To define an object parameters of type Parameters with the key (i.e. its name) my_parameters you can use this syntax :

  // Attempt to add parameters
  dolfin::Parameters parameters("my_parameters");

Concerning the call to to add, this function must be called inside the definition of another function in order to comply with C++ writing rules. Here is a stackoverflow post about that. For example:

[*In the cpp code snippet*]
  void add(std::string key, double pval)
  {
    my_parameters.add(key, pval);
  }
 // Function for setting a bool--valued parameter
  void add(std::string key, bool pval)
  {
    my_parameters.add(key, pval);
  }
  };

PYBIND11_MODULE(SIGNATURE, m)
{
  py::class_<Profile, std::shared_ptr<Profile>, dolfin::Expression>
    (m, "Profile")
    .def(py::init<>())
    .def("add_parameter", (void (Profile::*)(std::string, double)) &Profile::add, "Add double-valued parameter.")
    .def("add_parameter", (void (Profile::*)(std::string, bool)) &Profile::add, "Add bool-valued parameter.");
}
"""
[*In the python script*]
expr.add_parameter("p", 0.1)

However, I cannot use the modified code snippet to generate a FEniCS expression with my current installation. It generates an error similar to the one reported by Nico Schlömer a few weeks ago on this issue.

Thanks for the tips bd1747. I think I have made some progress, although defining the parameter set is a little different. The following code compiles.

from dolfin import *

cpp_code = """
#include <pybind11/pybind11.h>
namespace py = pybind11;

#include <dolfin/function/Expression.h>
#include <dolfin/parameter/Parameters.h>

class Profile : public dolfin::Expression
{
public:

  Profile() : dolfin::Expression(){}

  // New parameter set
  dolfin::Parameters my_parameters;

  void init_parameters()
  {
    my_parameters.add("p", 1.0);
  }

  void add(std::string key, double pval)
  {
    my_parameters.add(key, pval);
  }

};

PYBIND11_MODULE(SIGNATURE, m)
{
  py::class_<Profile, std::shared_ptr<Profile>, dolfin::Expression>
    (m, "Profile")
    .def(py::init<>())
    .def("add_parameter", (void (Profile::*)(std::string, double)) &Profile::add, "Add double-valued parameter.")
    .def("init_parameters", &Profile::init_parameters);
}
"""
# Compile expression
expr = CompiledExpression(compile_cpp_code(cpp_code).Profile(), degree=1)

# Attempt to add parameters
expr.init_parameters()
expr.add_parameter("q", 0.1)
print(expr.my_parameters)
print(expr.my_parameters.p)

However, the output yields:

>     None
>     Traceback (most recent call last):
>       File "parameterTest_pybind11.py", line 50, in <module>
>         print(expr.my_parameters.p)
>     AttributeError: 'NoneType' object has no attribute 'p'

It appears I am failing to add parameters.

To read a value of this Parameters instance you can use this syntax :

print(expr.my_parameters["p"])

Could you tell me which version of dolfin you are using, please ? I try to understand why I can’t create Expressions from cpp snippet anymore.

Oh this is confusing me now. I ran

docker run -ti -p 127.0.0.1:8000:8000 -v $(pwd):/home/fenics/shared -w /home/fenics/shared quay.io/fenicsproject/stable:current

assuming I would get fenics 2019.1. However,

fenics@ff101e53b6bc:~/shared$ dolfin-version
2018.1.0

If you are actually running 2019.1.0, then perhaps something with the Expressions class has broken…

Yes, I am actually running dolfin 2019.1.0, on Ubuntu. (Ubuntu 18.04, installed version of dolfin-bin: 2019.1.0-1~ppa1~bionic4).

OK, I am now running dolfin 2019.1.0 through docker, and I get the same error that I think you and Nico Schlömer are getting

ImportError: generic_type: type “Profile” referenced unknown base type “dolfin::Expression”

I see the Expression header file in the usual place: /usr/local/include/dolfin/function on my install.

psirus seems to have had a related issue a couple years ago https://bitbucket.org/fenics-apps/fenics-solid-mechanics/issues/14/importerror-generic_type-type. This was apparently resolved by rebuilding the pybind Python interface of Dolfin. Does anyone know if this is still an appropriate solution, and if so, how that can be achieved?