Topic: Python bindings and ownership

I worked through some memory safety in OOFEM, and I've come across the problems with "setDofManager" etc. in Domain.
This passea a raw pointer, of which Domain always takes ownership.
Python on the other hand doesn't have the concept of a single owner, nor can it actually give up ownership of a variable.
This causes problems when doing things like in test2.py

e1 = liboofem.beam2d(1, domain, nodes=(1,n2),  crossSect=1)
domain.setElement(e.number, e1)

Option 1 (my favorite):
1. Instead of creating the objects directly, we can set them via InputRecords. With dynamic input record, we could probably do something like

records += liboofem.InputRecord(1, "beam2d", {"nodes": [1,2], "crosssect": 1})
...
domain.instanciate(records)

something along these lines.

Another benefit is that we don't need to specialize each and every class we wish to expose (that alone makes this a winner I.M.H.O).
We could also bundle in postInitialize and such, so we don't leave the problem partially completed (which can be hard to debug).

2

Re: Python bindings and ownership

Maybe I am wrong, but I think one can make a derived class in python (derived from c++ base wrapped by boost) and pass it back to oofem. This could be quite useful. The proposed option would make this more difficult and some workaround will be needed anyway (like registering the class, etc).

I am trying to get python bindings running, as there is a issue reported on Github with test2.py, but there is no simple solution I could see at the moment.

Edit: Found a promising discussion on this problem
https://stackoverflow.com/questions/205 … unique-ptr
first response by Tanner Sansbury

Re: Python bindings and ownership

bp wrote:

Maybe I am wrong, but I think one can make a derived class in python (derived from c++ base wrapped by boost) and pass it back to oofem. This could be quite useful. The proposed option would make this more difficult and some workaround will be needed anyway (like registering the class, etc).

I'm not sure what you mean here; which class would be derived? The element?
It would need to be some kind of  wrapper that holds the unique_ptr, which can be "stolen" by OOFEM in a more-or-less clean way.

# Python code:
var  = liboofem.peakFunction(); # returns "FunctionWrapper(PeakFunction)" 
domain.setFunction(1, var);
x = var.evaluate(0.5)  # Trying to access var should now raise an exception in Python

Edit: Found a promising discussion on this problem
https://stackoverflow.com/questions/205 … unique-ptr
first response by Tanner Sansbury

I don't think this helps at all I'm afraid, that's a return value, which Python should take ownership of. This is fine. The opposite, taking ownership *from* python, can't ever be allowed.

I don't think there are any possible workarounds here. We could, at best, give the python variable a placeholder (e.g. nullptr) back after these methods are called.

---------

Still, I see only advantages with the approach using DynamicInputRecords (or a PythonInputRecord).
Especially since this also brings some other (rather massive) advantages (the ones I mentioned in the first post.
The syntax is basically just as nice, and we would save ourselves a staggering amount of coding.

I.M.H.O it would look nicer (this is just an example of what  we might end up with:

import liboofem

dr = liboofem.datareader()

dr.em = liboofem.ir(1, "beam2d", {"nodes": [1,2], "crosssect": 1})

dr.domain[0].nodes = [
    liboofem.ir(1, "node", {"coords": [0., 0., 0.]})
    liboofem.ir(2, "node", {"coords": [0., 1., 0.]})
    liboofem.ir(3, "node", {"coords": [0., 2., 0.]})
]

dr.domain[0].elements = [
    liboofem.ir(1, "beam2d", {"nodes": [1,2], "crosssect": 1})
    liboofem.ir(2, "beam2d", {"nodes": [1,2], "crosssect": 1})
    liboofem.ir(3, "beam2d", {"nodes": [1,2], "crosssect": 1})
]

dr.domain[1].boundaryconditions = [
    liboofem.ir(1, "boundarycondition", {"loadTimeFunction": 1,    "prescribedValue": 0})
]

etc.

problem=liboofem.InstanciateProblem(dr, liboofem.problemMode._processor, 0)

print("\nSolving problem")
problem.solveYourself()
problem.terminateAnalysis()
print("\nProblem solved")

It also has other nice advantages, such as being able to dump the problem to a normal OOFEMTXTDataReader file format if one needs to report a bug/do some debugging.