Git Product home page Git Product logo

sofapython3's Introduction

SofaPython3 plugin

Documentation Discord Support

This project is composed of a Sofa plugin to embed a python interpreter into a Sofa based simulation as well as several python modules that exposes the different c++ components used in Sofa. The binding is designed to be idiomatic python3 API with tight integration for numpy. This project is in a WIP state, please use it only if you are willing to help in the developement.

Installation

Requirement Install

  • pybind11 (minimal 2.3)
  • cmake (minimal 3.22)
  • developement package for python3 (python3-dev)

In-tree build

Add this directory path in CMAKE_EXTERNAL_DIRECTORIES.

NB: This plugin cannot be build through in-build process when the old SofaPython plugin is activated. To have both SofaPython3 and SofaPython you need to use out-of-tree build.

Out-of-tree build

This plugin should compile with out-of-tree builds. You might need to add the Sofa's installation path to the CMake prefix path. If you compiled Sofa in directory $SOFA_ROOT/build, consider doing an install step (make install, ninja install, etc.) and adding this installation path (example cmake -DCMAKE_PREFIX_PATH=$SOFA_ROOT/build/install/lib/cmake ..).

Changing the python path

The compilation of SofaPython3 plugin and bindings are tied to the python core library found during the CMake stage. To change the python version used for the compilation, you can either:

  1. Provide the python executable path with Python_EXECUTABLE cmake -DPython_EXECUTABLE=/usr/local/bin/python3 ..
  2. Provide the python root path with Python_ROOT_DIR cmake -DPython_ROOT_DIR=/usr/local ..

To see all the hints that can be provided to CMake, see the official CMake documentation on Python : https://cmake.org/cmake/help/latest/module/FindPython.html

Installing the python 3 bindings

In order to use SofaPython3 bindings directly into a python3 interpreter, Python needs to find the bindings libraries. This can be done automatically by going into the build directory of SofaPython3, and starting the cmake installation command:

plugin.SofaPython3/build $ cmake --install . 

This will first install the SofaPython3 plugin and bindings into the install directory, and then create symbolic links to the installed bindings into the python's user site-package directory (the directory returned by python3 -m site --user-site). After that, you should be able to import the SofaPython3 bindings directly into python:

$ python3
>>> import SofaRuntime
>>> import Sofa
>>> root = Sofa.Core.Node("root")

You can change the directory where the links are created by setting the cmake variable SP3_PYTHON_PACKAGES_LINK_DIRECTORY. For example, the following will create symbolic links into the /usr/lib/python3.8/dist-packages, hence making the SofaPython3 bindings available to python3 for all the system users.

plugin.SofaPython3/build $ cmake -DSP3_PYTHON_PACKAGES_LINK_DIRECTORY=/usr/lib/python3.8/dist-packages .
plugin.SofaPython3/build $ cmake --install . 

Finally, you can disable the automatic link creation with the cmake option SP3_LINK_TO_USER_SITE:

plugin.SofaPython3/build $ cmake -DSP3_LINK_TO_USER_SITE=OFF .

Features

The Sofa python module:

Expose the base Sofa object to make a scene.

  • binding of BaseObject, BaseNode, Base, BaseData [DONE]
  • copy-less API to access the sofa Data containers [WIP]
  • implement custom sofa object (ForceField, Controller) in python [POC]
  • docstring with sphinx content [WIP]

Try it: python import Sofa

The SofaRuntime python module:

  • access the runtime specific stuff (GUI, GLViewers, runSofa internal status) [POC]
  • docstring with sphinx content [TBD]

Try it: python import SofaRuntime

Developer's environment

  • autogenerated documentation using sphinx [DONE]
  • automated update the docs from the c++'s docstring: https://sofapython3.readthedocs.io/en/latest/ [WIP]
  • code completion with common editor [WIP, some editor are not working with c++ modules]

Execution environment:

  • SofaPython3 is a plugin to include a python3 environment in a Sofa scene [DONE],

Try it: xml <RequiredPlugin='SofaPython3'/>

  • Sofa and SofaRuntime are the python module that can be imported in any python interpreter (python3, ipython, jupyter)[DONE],

Try it: python python3 minimalscene.py

  • Access to Sofa simulation within the MathLab python interpreter [WIP-POC].
  • Make a full python GUI application (with UI framework like PySide2, pygame) and render an integrated sofa scene in an opengl context [POC]

Development history:

June 19, 2019

  • documentation extraction from .cpp (Thierry)
  • refactoring the modules & SofaRuntime (Jean Nicolas)
  • Data access & code cleaning (Bruno)
c1 = root.addChild("child1")
c2 = root.addChdil("child2")
o1 = root.addObject("MechanicalObject", dofs)
p = root.child1.child2.dofs.position         ## Slow acces to data 
p = root["child1.child2.dofs.position"]      ## Ffast access

sofapython3's People

Contributors

adagolodjo avatar albanodot avatar alxbilger avatar bakpaul avatar camille-k avatar coolteemf avatar damienmarchal avatar epernod avatar eulaliecoevoet avatar eve-le-guillou avatar fredroy avatar gitter-badger avatar guparan avatar hugtalbot avatar jnbrunet avatar marques-bruno avatar olivier-roussel avatar psomers3 avatar robinenjalbert avatar samfabrice avatar scheiklp avatar sescaida avatar tanguynav avatar tgaugry avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sofapython3's Issues

PluginManager does not find "python3" dir

Sofa's PluginManager does not find the "python3" dir

Description
The plugin Manager looks for a "python3/" dir inside the folder ../sofa/build/bin but this folder is in fact located in
../sofa/build/ . The path where the plugin manager looks for "python3/" dir is then wrong, generating an error.

Steps to reproduce

  • Compile the plugin SofaPython3 in tree, as explained in the documentation of SofaPython3 (the sofapython3.dll is created inside my ../sofa/build/bin/ folder, as it should be)
  • Open a terminal/command window (cmd)
  • Type runSofa.exe
  • Enter the "Edit>PluginManager" tab

Expected behavior
The plugin SofaPython3 should have been added to my SofaEnvironment succesfully, and I should have been able to use to load Python3 scenes without doing anything more.


Environment

Context

  • System: Windows 10
  • Version of SOFA: v20.12 branch at commit ee7664e, v20.12.00 binaries
  • State: build directory

Command called

runSofa.exe -l SofaPython3.dll

Env vars

  echo "SOFA_ROOT = C:\Users\jducr\Documents\Librairies\sofa\build\bin\Release"
  echo "PYTHONPATH = C:\Users\jducr\Documents\Librairies\Python\Python37"
  echo "python3 -V = Python 3.7.7"
No python3 dir found in .
No python3 dir found in C:\Users\jducr\Documents\Librairies\\sofa\build\bin

Logs

Full output

Microsoft Windows [version 10.0.19043.985]
(c) Microsoft Corporation. Tous droits réservés.

C:\Users\jducr>cd C:\Users\jducr\Documents\Librairies\sofa\build\bin\Release

C:\Users\jducr\Documents\Librairies\sofa\build\bin\Release>runSofa -l SofaPython3.dll
[INFO]    [runSofa] PluginRepository paths = C:/Users/jducr/Documents/Librairies/sofa/build/plugins;C:/Users/jducr/Documents/Librairies/sofa/build/bin;C:/Users/jducr/Documents/Librairies/sofa/build/bin/Release
[INFO]    [runSofa] DataRepository paths = C:/Users/jducr/Documents/Librairies/sofa/src/share;C:/Users/jducr/Documents/Librairies/sofa/src/examples;C:/Users/jducr/Documents/Librairies/sofa/build;C:/Users/jducr/Documents/Librairies/sofa/build
[INFO]    [runSofa] GuiDataRepository paths = C:/Users/jducr/Documents/Librairies/sofa/src/applications/projects/runSofa/resources;C:/Users/jducr/Documents/Librairies/sofa/src/modules/SofaGuiQt/src/sofa/gui/qt/resources
[INFO]    [SofaPython3]  Initializing with python version 3.7.7 (tags/v3.7.7:d7c567b08f, Mar 10 2020, 10:41:24) [MSC v.1900 64 bit (AMD64)]
[INFO]    [SofaPython3] Registering a scene loader for [.py, .py3, .pyscn, .py3scn] files.
[INFO]    [SofaPython3] Intializing python
[INFO]    [SofaPython3] Added 'C:/Users/jducr/Documents/Librairies/sofa/src/applications/plugins/SofaTest/python' to sys.path
[INFO]    [SofaPython3] No python3 dir found in .

########## SIG 22 - SIGABRT: usually caused by an abort() or assert() ##########
21: sofa::helper::BackTrace::dump - 0x7ffa3156a41020: sofa::helper::BackTrace::sig - 0x7ffa3156a57019: raise - 0x7ffa695816a018: abort - 0x7ffa6958282017: terminate - 0x7ffa69581f8016: terminate - 0x7ffa69581f8015: UnhandledExceptionFilter - 0x7ffa6916b6a014: memset - 0x7ffa6b973ec013: _C_specific_handler - 0x7ffa6b95c6e012: _chkstk - 0x7ffa6b97206011: RtlRaiseException - 0x7ffa6b92102010: RtlRaiseException - 0x7ffa6b9210209: RaiseException - 0x7ffa69094b208: CxxThrowException - 0x7ffa0d1b63f07: sofapython3::PythonEnvironment::Init - 0x7ffa23c737b06: initExternalModule - 0x7ffa23c6fa705: sofa::helper::system::PluginManager::loadPluginByPath - 0x7ffa316052104: sofa::helper::system::PluginManager::loadPlugin - 0x7ffa31604b603: sofa::helper::system::PluginManager::loadPlugin - 0x7ffa31604b602: sofa::helper::system::PluginManager::loadPlugin - 0x7ffa31604b601: BaseThreadInitThunk - 0x7ffa69db70200: RtlUserThreadStart - 0x7ffa6b922630[WARNING] [SofaSimulationTree] the library has not been cleaned up (sofa::simulation::tree::cleanup() has never been called, see sofa/helper/init.h)
[WARNING] [SofaSimulationGraph] the library has not been cleaned up (sofa::simulation::graph::cleanup() has never been called, see sofa/helper/init.h)
[WARNING] [SofaSimulationCore] the library has not been cleaned up (sofa::simulation::core::cleanup() has never been called, see sofa/helper/init.h)
[WARNING] [SofaCore] the library has not been cleaned up (sofa::core::cleanup() has never been called, see sofa/helper/init.h)

Content of build_dir/CMakeCache.txt

CMakeCache.txt

Memory Leak related to unload and rendering

Hi,
there is another memory leak, this time related to rendering in combination with simulation.unload.

As a testing scene, I took the pygame example from SofaPython3 and added a reload to every tenth step.

import Sofa
import Sofa.Core
import Sofa.Simulation
import Sofa.SofaGL
import SofaRuntime
import os

sofa_directory = os.environ["SOFA_ROOT"]
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *

display_size = (800, 600)


def init_display(node):
    pygame.display.init()
    pygame.display.set_mode(display_size, pygame.DOUBLEBUF | pygame.OPENGL)

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glEnable(GL_LIGHTING)
    glEnable(GL_DEPTH_TEST)
    Sofa.SofaGL.glewInit()
    Sofa.Simulation.initVisual(node)
    Sofa.Simulation.initTextures(node)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, (display_size[0] / display_size[1]), 0.1, 50.0)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()


def simple_render(rootNode):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glEnable(GL_LIGHTING)
    glEnable(GL_DEPTH_TEST)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, (display_size[0] / display_size[1]), 0.1, 50.0)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    cameraMVM = rootNode.camera.getOpenGLModelViewMatrix()
    glMultMatrixd(cameraMVM)
    Sofa.SofaGL.draw(rootNode)

    pygame.display.get_surface().fill((0, 0, 0))
    pygame.display.flip()


def createScene(root):
    # Register all the common component in the factory.
    SofaRuntime.PluginRepository.addFirstPath(os.path.join(sofa_directory, "bin"))
    root.addObject("RequiredPlugin", name="SofaOpenglVisual")  # visual stuff
    root.addObject("RequiredPlugin", name="SofaLoader")  # geometry loaders
    root.addObject("RequiredPlugin", name="SofaSimpleFem")  # diffusion fem
    root.addObject("RequiredPlugin", name="SofaBoundaryCondition")  # constraints
    root.addObject("RequiredPlugin", name="SofaEngine")  # Box Roi
    root.addObject("RequiredPlugin", name="SofaImplicitOdeSolver")  # implicit solver
    root.addObject("RequiredPlugin", name="SofaMiscForceField")  # meshmatrix
    root.addObject("RequiredPlugin", name="SofaGeneralEngine")  # TextureInterpolation
    root.addObject("RequiredPlugin", name="CImgPlugin")  # for loading a bmp image for texture
    root.addObject("RequiredPlugin", name="SofaBaseLinearSolver")
    root.addObject("RequiredPlugin", name="SofaGeneralVisual")
    root.addObject("RequiredPlugin", name="SofaTopologyMapping")
    root.addObject("RequiredPlugin", name="SofaGeneralTopology")
    root.addObject("RequiredPlugin", name="SofaGeneralLoader")

    ### these are just some things that stay still and move around
    # so you know the animation is actually happening
    root.gravity = [0, -1.0, 0]
    root.addObject("VisualStyle", displayFlags="showAll")
    root.addObject("MeshGmshLoader", name="meshLoaderCoarse", filename="mesh/liver.msh")
    root.addObject("MeshObjLoader", name="meshLoaderFine", filename="mesh/liver-smooth.obj")

    root.addObject("EulerImplicitSolver")
    root.addObject("CGLinearSolver", iterations="200", tolerance="1e-09", threshold="1e-09")

    liver = root.addChild("liver")

    liver.addObject("TetrahedronSetTopologyContainer", name="topo", src="@../meshLoaderCoarse")
    liver.addObject("TetrahedronSetGeometryAlgorithms", template="Vec3d", name="GeomAlgo")
    liver.addObject("MechanicalObject", template="Vec3d", name="MechanicalModel", showObject="1", showObjectScale="3")

    liver.addObject("TetrahedronFEMForceField", name="fem", youngModulus="1000", poissonRatio="0.4", method="large")

    liver.addObject("MeshMatrixMass", massDensity="1")
    liver.addObject("FixedConstraint", indices="2 3 50")
    visual = liver.addChild("visual")
    visual.addObject("MeshObjLoader", name="meshLoader_0", filename="mesh/liver-smooth.obj", handleSeams="1")
    visual.addObject("OglModel", name="VisualModel", src="@meshLoader_0", color="red")
    visual.addObject("BarycentricMapping", input="@..", output="@VisualModel", name="visual mapping")

    # place light and a camera
    root.addObject("LightManager")
    root.addObject("DirectionalLight", direction=[0, 1, 0])
    root.addObject("InteractiveCamera", name="camera", position=[0, 15, 0], lookAt=[0, 0, 0], distance=37, fieldOfView=45, zNear=0.63, zFar=55.69)


if __name__ == "__main__":

    root = Sofa.Core.Node("myroot")
    createScene(root)
    Sofa.Simulation.init(root)

    with_window = True
    if with_window:
        init_display(root)

    for i in range(300):
        Sofa.Simulation.animate(root, root.getDt())
        Sofa.Simulation.updateVisual(root)
        if with_window:
            simple_render(root)
        if i % 10 == 0:
            if with_window:
                pygame.display.quit()
            Sofa.Simulation.unload(root)
            createScene(root)
            Sofa.Simulation.init(root)
            if with_window:
                init_display(root)

The leak seems to be related to the OpenGL context, because for 31 init_display calls, I get and AddressSanitizer output of

Indirect leak of 59521240 byte(s) in 31 object(s) allocated from:
    #0 0x7f8a1c4cf808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7f8a0be337c3 in SDL_malloc_REAL /sdl_build/SDL2-2.0.16/src/stdlib/SDL_malloc.c:5388

that matches the lost amount of memory.
And it also veeeery closely matches the amount of memory required to store 31 RGBA buffers of the set image size. :D

>>> (600*800*4*31)/59521240
0.9999791671006854

If no display is created, there is no leak.

@fredroy Do you maybe know what could be the issue here?

Cheers,
Paul

Detect pybind11 version mismatches

Thanks to @coolteemf who reported this issue.

When we are creating our own bindings inside an external plugin, class inheritance against a base class defined in SofaPython3 is possible if and only if the pybind11 version is the same for both projects.

From pybind11 documentation:

Note also that it is possible (although would rarely be required) to share arbitrary C++ objects between extension modules at runtime. Internal library data is shared between modules using capsule machinery 1 which can be also utilized for storing, modifying and accessing user-defined data. Note that an extension module will “see” other extensions’ data if and only if they were built with the same pybind11 version.

For example, the following:

┌─────────────────┐      ┌────────────────────────┐
│    MyPlugin     │      │       SofaPython3      │
│                 │      │                        │
│   Mycamera <-------------- BaseCamera           │
│                 │      │                        │
│                 │      └────────────────────────┘
└─────────────────┘
from Sofa.Core import Camera
from MyPlugin import MyCamera

is only possible if SofaPython3 and MyPlugin is compiled using exactly the same pybind11 version. Else, the following will happen:

>>> from Sofa.Core import Camera
>>> from MyPlugin  import MyCamera
[ERROR]   [SofaRuntime] ImportError: generic_type: type "MyCamera" referenced unknown base type "sofa::component::visualmodel::BaseCamera"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>

This error is not so trivial to debug, and we could easily detect it at the CMake stage by verifying pybind11 version match. We could, for example, insert the current pybind11 version inside an exported CMake's target property. @guparan, do you think this could be feasible?

Loading SOFA's dlls starting from Python3.8

After a few hours to figure out why my python install was not finding Sofa's DLLs (despite the fact that I set the PATH correctly), it appears that, starting from 3.8, python changes its way to load DLLs: https://stackoverflow.com/a/64303856

DLL dependencies for extension modules and DLLs loaded with ctypes on Windows are now resolved more securely. Only the system paths, the directory containing the DLL or PYD file, and directories added with add_dll_directory() are searched for load-time dependencies. Specifically, PATH and the current working directory are no longer used, and modifications to these will no longer have any effect on normal DLL resolution. If your application relies on these mechanisms, you should check for add_dll_directory() and if it exists, use it to add your DLLs directory while loading your library.

So now this is the new way to make it work in python 3.8 and later:
---
import os
os.add_dll_directory('my-app-dir')
---
Again, the old way is still correct and you will have to use it in python 3.7 and older:
---
import os
os.environ['PATH'] = 'my-app-dir' + os.pathsep + os.environ['PATH']
---

For now, a possible workaround in your present py3 script (if python >= 3.8)

import os
os.add_dll_directory('C:\\Work\\sofa\\install\\bin')

import Sofa
.........

Expose my own types to python

In my plugin I have derived a new type from BaseObject.

class BlendingField : public sofa::core::objectmodel::BaseObject
{
    virtual double blending(const Eigen::Vector3d &p) const = 0;
}

In order to implemente this, I tried to add python bindings

class BlendingField : public VNCS::BlendingField
{
public:
    SOFA_CLASS(VNCS::py::BlendingField, VNCS::BlendingField);
    BlendingField();
    ~BlendingField() override;

    VNCS::Real blending(const VNCS::Point_3 &p) const final;
};
...
VNCS::Real VNCS::py::BlendingField::blending(const VNCS::Point_3 &p) const
{
    const std::array<double, 3> pyP{p[0], p[1], p[2]};
    PYBIND11_OVERLOAD_PURE(VNCS::Real,          /* Return type */
                           VNCS::BlendingField, /* Parent class */
                           blending,            /* Name of function in C++ (must match Python name) */
                           pyP                  /* Argument(s) */
    );
}

void VNCS::py::module::blendingField(pybind11::module &m)
{
pybind11::class_<VNCS::BlendingField, VNCS::py::BlendingField>(m, "BlendingField")
.def(pybind11::init([](pybind11::args &args, pybind11::kwargs &kwargs) {
return new VNCS::py::BlendingField();
}))
.def("blending", &VNCS::BlendingField::blending);
}
This works fine in the sense that I can derive from BlendingField in SOFA, but I can't add it to a SOFA node, with the following error:

[ERROR]   [SofaPython3::SceneLoader] TypeError: addObject(): incompatible function arguments. The following argument types are supported:
   1. (self: Sofa.Core.Node, arg0: str, **kwargs) -> object
   2. (self: Sofa.Core.Node, arg0: Sofa.Core.Object) -> object

Invoked with: <Sofa.Core.Node object at 0x7fffac34bef0>, <prepare_scene.TestBlendingField object at 0x7fffac34bea0>

Somehow, it looks that SOFA can't see the my class derives from sofa::core::objectmodel::BaseObject. I have tried to find any example of any other plugin creating its own bindings but couldn't find any.
I added some log output to the ctor and dtor of the BlendingField and it shows that its being destroyed inmediately.
Any help with this would be amazing.

The python part of overloaded objects are deleted

The issue is related to python reference holding in overridable classes.

Some binded have a valid behavior (eg: Controller) but other don't (Prefab/RawPrefab)
The problem can be reproduced with the follwing scene:

import Sofa

class MyController(Sofa.Core.Controller):
    def __init__(self, *args, **kwargs):
        Sofa.Core.Controller.__init__(self,*args,**kwargs)
        print("Controller created")

    def __del__(self):
        print("Controller deleted")

class MyPrefab(Sofa.Core.RawPrefab):
    def __init__(self, *args, **kwargs):
        Sofa.Core.RawPrefab.__init__(self,*args,**kwargs)
        print("Prefab created")

    def __del__(self):
        print("Prefab deleted")

def createScene(root):
    root.addObject(MyController())    
    root.addChild(MyPrefab())   

The expected behavior is demonstrated by overriding a Controller, and the broken is implmented by overriding a Prefab.
The MyPrefab's destructor is called (which means that the reference count down to zero), while the controller's one is not.

This is related probably related to PR: #74
(Adding manually an extra reference to the python object solve the problem but is clearly is not the solution... I wonder if this is due to a misunderstanding in how the py_shared_ptr in Bindin_Prefab.cpp)

Any idea @jnbrunet, @fredroy ?

Rename this repo into "SofaPython3"

Click on the 👍 below if you are ready for this change.

Naming plugins with "plugin." is not needed since we can add labels to repositories.

From GitHub documentation:

All git clone, git fetch, or git push operations targeting the previous location will continue to function as if made on the new location. However, to reduce confusion, we strongly recommend updating any existing local clones to point to the new repository URL.

The command to run in your SofaPython3 clone:
git remote set-url origin https://github.com/sofa-framework/SofaPython3

Memory leak in DataContainer

Hi!
After talking to @etagliabue a bit about sofa-framework/sofa#3088 I discovered a memory leak that occurs when reading from a DataContainer.

For a given MechanicalObject, different ways of accessing the array of positions create a leak.

The methods that I tested so far, that create a leak are:

MO.position.array()

with MO.position.writeable() as state:

MO.position.value

image

Accessing the array through DataContainer.__getitem__ and DataContainer.toList() do not create the leak. Weirdly enough, it is also a writeable view of the array, which DataContainer.value.__getitem__ is not.

state = MO.position[:]
state[0, 0] = np.random.rand()

MO.position.toList()

image

I did these tests by profiling this scene

import Sofa
import Sofa.SofaGL
import SofaRuntime
import Sofa.Simulation
import os
from tqdm import tqdm
import numpy as np


def createScene(root):
    # Register all the common component in the factory.
    sofa_directory = os.environ["SOFA_ROOT"]
    SofaRuntime.PluginRepository.addFirstPath(os.path.join(sofa_directory, "bin"))
    root.addObject("RequiredPlugin", name="SofaOpenglVisual")  # visual stuff
    root.addObject("RequiredPlugin", name="SofaLoader")  # geometry loaders
    root.addObject("RequiredPlugin", name="SofaSimpleFem")  # diffusion fem
    root.addObject("RequiredPlugin", name="SofaBoundaryCondition")  # constraints
    root.addObject("RequiredPlugin", name="SofaEngine")  # Box Roi
    root.addObject("RequiredPlugin", name="SofaImplicitOdeSolver")  # implicit solver
    root.addObject("RequiredPlugin", name="SofaMiscForceField")  # meshmatrix
    root.addObject("RequiredPlugin", name="SofaGeneralEngine")  # TextureInterpolation
    root.addObject("RequiredPlugin", name="CImgPlugin")  # for loading a bmp image for texture
    root.addObject("RequiredPlugin", name="SofaBaseLinearSolver")
    root.addObject("RequiredPlugin", name="SofaGeneralVisual")
    root.addObject("RequiredPlugin", name="SofaTopologyMapping")
    root.addObject("RequiredPlugin", name="SofaGeneralTopology")
    root.addObject("RequiredPlugin", name="SofaGeneralLoader")

    ### these are just some things that stay still and move around
    # so you know the animation is actually happening
    root.gravity = [0, -1.0, 0]
    root.addObject("VisualStyle", displayFlags="showAll")

    root.addObject("EulerImplicitSolver")
    root.addObject("CGLinearSolver", iterations="200", tolerance="1e-09", threshold="1e-09")

    for i in range(2):
        liver = root.addChild(f"liver{i}")

        liver.addObject("MeshGmshLoader", name="meshLoaderCoarse", filename="mesh/liver.msh", translation=[i * 10, 0, 0])

        liver.addObject("TetrahedronSetTopologyContainer", name="topo", src="@meshLoaderCoarse")
        liver.addObject("TetrahedronSetGeometryAlgorithms", template="Vec3d", name="GeomAlgo")
        liver.addObject("MechanicalObject", template="Vec3d", name="MechanicalModel", showObject="1", showObjectScale="3")

        liver.addObject("TetrahedronFEMForceField", name="fem", youngModulus="1000", poissonRatio="0.4", method="large")

        liver.addObject("MeshMatrixMass", massDensity="1")
        liver.addObject("FixedConstraint", indices="2 3 50")
        visual = liver.addChild("visual")
        visual.addObject("MeshObjLoader", name="meshLoader_0", filename="mesh/liver-smooth.obj", handleSeams="1", translation=[i * 10, 0, 0])
        visual.addObject("OglModel", name="VisualModel", src="@meshLoader_0", color="red")
        visual.addObject("BarycentricMapping", input="@..", output="@VisualModel", name="visual mapping")

    # place light and a camera
    root.addObject("LightManager")
    root.addObject("DirectionalLight", direction=[0, 1, 0])
    root.addObject("InteractiveCamera", name="camera", position=[0, 15, 0], lookAt=[0, 0, 0], distance=37, fieldOfView=45, zNear=0.63, zFar=55.69)



if __name__ == "__main__":
    root = Sofa.Core.Node("myroot")
    root.dt.value = 0.01
    createScene(root)

    Sofa.Simulation.init(root)

    for i in tqdm(range(int(1e5))):
        Sofa.Simulation.animate(root, root.getDt())

        # Leaking methods:
        # - root.liver0.MechanicalModel.position.value[:]
        # - root.liver0.MechanicalModel.position.array()
        # - with root.liver0.MechanicalModel.position.writeable() as state:
        #     pass

        # Non-leaking methods:
        # - root.liver0.MechanicalModel.position.toList()
        # - root.liver0.MechanicalModel.position[:]

with https://github.com/pythonprofilers/memory_profiler
-> mprof run --output results.dat --python python scene.py
-> mprof plot results.dat

Plugin.SofaPython3: Compilation problem

/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11_static_set': <artificial>:(.text+0x57): undefined reference to PyProperty_Type'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11_static_get': <artificial>:(.text+0x83): undefined reference to PyProperty_Type'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11_object_init': <artificial>:(.text+0x1429): undefined reference to PyExc_TypeError'
:(.text+0x1435): undefined reference to PyErr_SetString' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cast_error::set_error() const':
:(.text+0x1490): undefined reference to PyExc_RuntimeError' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::type_caster<bool, void>& pybind11::detail::load_type<bool, void>(pybind11::detail::type_caster<bool, void>&, pybind11::handle const&)':
:(.text+0x1894): undefined reference to _Py_TrueStruct' <artificial>:(.text+0x18a1): undefined reference to _Py_FalseStruct'
:(.text+0x18aa): undefined reference to _Py_NoneStruct' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::type_caster<double, void>::load(pybind11::handle, bool)':
:(.text+0x19d5): undefined reference to PyFloat_Type' <artificial>:(.text+0x19df): undefined reference to PyType_IsSubtype'
:(.text+0x19ef): undefined reference to PyFloat_AsDouble' <artificial>:(.text+0x1a27): undefined reference to PyErr_Occurred'
:(.text+0x1a39): undefined reference to PyExc_TypeError' <artificial>:(.text+0x1a41): undefined reference to PyErr_ExceptionMatches'
:(.text+0x1a4a): undefined reference to PyErr_Clear' <artificial>:(.text+0x1a57): undefined reference to PyNumber_Check'
:(.text+0x1a63): undefined reference to PyNumber_Float' <artificial>:(.text+0x1a6b): undefined reference to PyErr_Clear'
:(.text+0x1ab1): undefined reference to PyErr_Clear' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function std::__cxx11::basic_string<char, std::char_traits, std::allocator > pybind11::cast<std::__cxx11::basic_string<char, std::char_traits, std::allocator >, 0>(pybind11::handle const&)':
:(.text+0x1b48): undefined reference to PyUnicode_AsEncodedString' <artificial>:(.text+0x1b5c): undefined reference to PyBytes_AsString'
:(.text+0x1b67): undefined reference to PyBytes_Size' <artificial>:(.text+0x1c51): undefined reference to PyErr_Clear'
:(.text+0x1cab): undefined reference to PyBytes_AsString' <artificial>:(.text+0x1cbb): undefined reference to PyBytes_Size'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11_meta_getattro': <artificial>:(.text+0x236d): undefined reference to _PyType_Lookup'
:(.text+0x2379): undefined reference to PyType_Type' <artificial>:(.text+0x23a3): undefined reference to PyInstanceMethod_Type'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::translate_exception(std::__exception_ptr::exception_ptr)': <artificial>:(.text+0x24bf): undefined reference to PyExc_RuntimeError'
:(.text+0x24ce): undefined reference to PyErr_SetString' <artificial>:(.text+0x24f4): undefined reference to PyExc_RuntimeError'
:(.text+0x24fc): undefined reference to PyErr_SetString' <artificial>:(.text+0x2536): undefined reference to PyExc_ValueError'
:(.text+0x253e): undefined reference to PyErr_SetString' <artificial>:(.text+0x2558): undefined reference to PyExc_IndexError'
:(.text+0x2560): undefined reference to PyErr_SetString' <artificial>:(.text+0x259d): undefined reference to PyExc_ValueError'
:(.text+0x25a5): undefined reference to PyErr_SetString' <artificial>:(.text+0x25c2): undefined reference to PyExc_ValueError'
:(.text+0x25ca): undefined reference to PyErr_SetString' <artificial>:(.text+0x2607): undefined reference to PyExc_ValueError'
:(.text+0x260f): undefined reference to PyErr_SetString' <artificial>:(.text+0x262c): undefined reference to PyExc_MemoryError'
:(.text+0x2634): undefined reference to PyErr_SetString' <artificial>:(.text+0x269a): undefined reference to PyErr_Restore'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::capsule::capsule(void const*, void (*)(void*))::{lambda(_object*)#1}::_FUN(_object*)': <artificial>:(.text+0x296a): undefined reference to PyCapsule_GetContext'
:(.text+0x2977): undefined reference to PyCapsule_GetPointer' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::tuple::tuple(unsigned long)':
:(.text+0x2a0d): undefined reference to PyTuple_New' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::str::str(char const*)':
:(.text+0x2a5d): undefined reference to PyUnicode_FromString' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::str::operator std::__cxx11::basic_string<char, std::char_traits, std::allocator >() const':
:(.text+0x2ac8): undefined reference to PyUnicode_AsUTF8String' <artificial>:(.text+0x2af3): undefined reference to PyBytes_AsStringAndSize'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::accessor<pybind11::detail::accessor_policies::str_attr>::get_cache() const': <artificial>:(.text+0x2b99): undefined reference to PyObject_GetAttrString'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::type_caster_generic::try_load_foreign_module_local(pybind11::handle)': <artificial>:(.text+0x2c2f): undefined reference to PyObject_HasAttrString'
:(.text+0x2c5b): undefined reference to PyObject_GetAttrString' <artificial>:(.text+0x2c73): undefined reference to PyCapsule_GetName'
:(.text+0x2c7e): undefined reference to PyCapsule_GetPointer' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::get_internals()':
:(.text+0x2df1): undefined reference to PyGILState_Ensure' <artificial>:(.text+0x2dfa): undefined reference to PyEval_GetBuiltins'
:(.text+0x2e49): undefined reference to PyUnicode_DecodeUTF8' <artificial>:(.text+0x2e7b): undefined reference to PyTuple_New'
:(.text+0x2ea3): undefined reference to PyObject_CallObject' <artificial>:(.text+0x301e): undefined reference to PyEval_InitThreads'
:(.text+0x3023): undefined reference to PyThreadState_Get' <artificial>:(.text+0x302f): undefined reference to PyThread_tss_alloc'
:(.text+0x304f): undefined reference to PyThread_tss_create' <artificial>:(.text+0x306a): undefined reference to PyThread_tss_set'
:(.text+0x3089): undefined reference to PyCapsule_New' <artificial>:(.text+0x30a1): undefined reference to PyUnicode_FromString'
:(.text+0x30cb): undefined reference to PyObject_SetItem' <artificial>:(.text+0x312b): undefined reference to PyUnicode_FromString'
:(.text+0x3132): undefined reference to PyType_Type' <artificial>:(.text+0x318e): undefined reference to PyProperty_Type'
:(.text+0x31ba): undefined reference to PyType_Ready' <artificial>:(.text+0x31e5): undefined reference to PyObject_SetAttrString'
:(.text+0x323a): undefined reference to PyUnicode_FromString' <artificial>:(.text+0x32bc): undefined reference to PyType_Ready'
:(.text+0x32e7): undefined reference to PyObject_SetAttrString' <artificial>:(.text+0x333e): undefined reference to PyUnicode_FromString'
:(.text+0x33ae): undefined reference to PyBaseObject_Type' <artificial>:(.text+0x33e5): undefined reference to PyType_Ready'
:(.text+0x3410): undefined reference to PyObject_SetAttrString' <artificial>:(.text+0x3460): undefined reference to PyGILState_Release'
:(.text+0x34bd): undefined reference to PyObject_GetItem' <artificial>:(.text+0x351d): undefined reference to PyCapsule_Type'
:(.text+0x3554): undefined reference to PyObject_GetItem' <artificial>:(.text+0x3593): undefined reference to PyCapsule_GetName'
:(.text+0x359e): undefined reference to PyCapsule_GetPointer' <artificial>:(.text+0x3644): undefined reference to PyGILState_Release'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::type_caster_generic::src_and_type(void const*, std::type_info const&, std::type_info const*) [clone .constprop.31]': <artificial>:(.text+0x3d2b): undefined reference to PyExc_TypeError'
:(.text+0x3d38): undefined reference to PyErr_SetString' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize_generic(pybind11::detail::function_record*, char const*, std::type_info const* const*, unsigned long)':
:(.text+0x3ee3): undefined reference to PyTuple_New' <artificial>:(.text+0x3f03): undefined reference to PyObject_CallObject'
:(.text+0x43e0): undefined reference to PyInstanceMethod_Type' <artificial>:(.text+0x43ed): undefined reference to PyCFunction_Type'
:(.text+0x43fa): undefined reference to _Py_NoneStruct' <artificial>:(.text+0x4450): undefined reference to PyCapsule_New'
:(.text+0x446b): undefined reference to PyCapsule_SetContext' <artificial>:(.text+0x448c): undefined reference to PyObject_HasAttrString'
:(.text+0x44a5): undefined reference to PyObject_HasAttrString' <artificial>:(.text+0x44bc): undefined reference to PyCFunction_NewEx'
:(.text+0x4914): undefined reference to PyUnicode_AsEncodedString' <artificial>:(.text+0x4928): undefined reference to PyBytes_AsString'
:(.text+0x4935): undefined reference to PyBytes_Size' <artificial>:(.text+0x4aa8): undefined reference to PyCFunction_Type'
:(.text+0x4ac6): undefined reference to PyCapsule_GetName' <artificial>:(.text+0x4ad1): undefined reference to PyCapsule_GetPointer'
:(.text+0x4bf9): undefined reference to PyInstanceMethod_New' <artificial>:(.text+0x4c3a): undefined reference to PyCFunction_NewEx'
:(.text+0x4e82): undefined reference to PyCFunction_NewEx' <artificial>:(.text+0x4f89): undefined reference to PyErr_Clear'
:(.text+0x5025): undefined reference to PyBytes_AsString' <artificial>:(.text+0x5039): undefined reference to PyBytes_Size'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize<pybind11::detail::all_type_info_get_cache(_typeobject*)::{lambda(pybind11::handle)#1}, void, pybind11::handle>(pybind11::detail::all_type_info_get_cache(_typeobject*)::{lambda(pybind11::handle)#1}&&, void (*)(pybind11::handle))::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call)': <artificial>:(.text+0x5a99): undefined reference to _Py_NoneStruct'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::gil_scoped_acquire::dec_ref()': <artificial>:(.text+0x5b38): undefined reference to PyThreadState_Clear'
:(.text+0x5b3d): undefined reference to PyThreadState_DeleteCurrent' <artificial>:(.text+0x5b50): undefined reference to PyThread_tss_set'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::gil_scoped_acquire::gil_scoped_acquire()': <artificial>:(.text+0x5bb4): undefined reference to PyThread_tss_get'
:(.text+0x5bc1): undefined reference to _PyThreadState_UncheckedGet' <artificial>:(.text+0x5bd6): undefined reference to PyEval_AcquireThread'
:(.text+0x5bf1): undefined reference to PyGILState_GetThisThreadState' <artificial>:(.text+0x5c05): undefined reference to PyThreadState_New'
:(.text+0x5c21): undefined reference to PyThread_tss_set' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::error_already_set::~error_already_set()':
:(.text+0x5c67): undefined reference to PyErr_Fetch' <artificial>:(.text+0x5cd5): undefined reference to PyErr_Restore'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::error_already_set::~error_already_set()': <artificial>:(.text+0x5d77): undefined reference to PyErr_Fetch'
:(.text+0x5de5): undefined reference to PyErr_Restore' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11_meta_setattro':
:(.text+0x5e62): undefined reference to _PyType_Lookup' <artificial>:(.text+0x5e76): undefined reference to PyType_Type'
:(.text+0x5eae): undefined reference to PyObject_IsInstance' <artificial>:(.text+0x5ebd): undefined reference to PyObject_IsInstance'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::all_type_info(_typeobject*)': <artificial>:(.text+0x64c7): undefined reference to PyWeakref_NewRef'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::instance::allocate_layout()': <artificial>:(.text+0x67c1): undefined reference to PyMem_Calloc'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::type_caster_generic::cast(void const*, pybind11::return_value_policy, pybind11::handle, pybind11::detail::type_info const*, void* (*)(void const*), void* (*)(void const*), void const*) [clone .constprop.9]': <artificial>:(.text+0x69b3): undefined reference to _Py_NoneStruct'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize<sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)#6}, pybind11::object, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, pybind11::name, pybind11::scope, pybind11::sibling, char const*>(sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)#6}&&, pybind11::object (*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&), pybind11::name const&, pybind11::scope const&, pybind11::sibling const&, char const* const&)::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call)': <artificial>:(.text+0x6aad): undefined reference to PyUnicode_AsEncodedString'
:(.text+0x6ac1): undefined reference to PyBytes_AsString' <artificial>:(.text+0x6acc): undefined reference to PyBytes_Size'
:(.text+0x6d73): undefined reference to _Py_NoneStruct' <artificial>:(.text+0x6d94): undefined reference to PyBytes_AsString'
:(.text+0x6da8): undefined reference to PyBytes_Size' <artificial>:(.text+0x6e51): undefined reference to PyErr_Clear'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::get_type_info(_typeobject*)': <artificial>:(.text+0x72f7): undefined reference to PyWeakref_NewRef'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::dispatcher(_object*, _object*, _object*)': <artificial>:(.text+0x77fc): undefined reference to PyCapsule_GetPointer'
:(.text+0x78cb): undefined reference to PyDict_GetItemString' <artificial>:(.text+0x7c09): undefined reference to PyDict_Size'
:(.text+0x7d6a): undefined reference to _Py_NoneStruct' <artificial>:(.text+0x85dd): undefined reference to PyDict_GetItemString'
:(.text+0x8606): undefined reference to PyDict_Copy' <artificial>:(.text+0x8642): undefined reference to PyDict_DelItemString'
:(.text+0x8c69): undefined reference to _Py_NoneStruct' <artificial>:(.text+0x907c): undefined reference to PyDict_New'
:(.text+0x90d1): undefined reference to _Py_NotImplementedStruct' <artificial>:(.text+0x91fe): undefined reference to PyTuple_SetItem'
:(.text+0x9299): undefined reference to PyTuple_Size' <artificial>:(.text+0x92b1): undefined reference to PyTuple_GetItem'
:(.text+0x92c9): undefined reference to PyObject_Repr' <artificial>:(.text+0x9368): undefined reference to PyTuple_Size'
:(.text+0x941c): undefined reference to PyDict_Size' <artificial>:(.text+0x948b): undefined reference to PyDict_Next'
:(.text+0x957c): undefined reference to PyDict_Next' <artificial>:(.text+0x9643): undefined reference to PyObject_CallObject'
:(.text+0x9686): undefined reference to PyObject_Str' <artificial>:(.text+0x9707): undefined reference to PyExc_TypeError'
:(.text+0x9717): undefined reference to PyErr_SetString' <artificial>:(.text+0x9cb9): undefined reference to PyExc_TypeError'
:(.text+0x9cca): undefined reference to PyErr_SetString' <artificial>:(.text+0x9d32): undefined reference to PyExc_TypeError'
:(.text+0x9d42): undefined reference to PyErr_SetString' <artificial>:(.text+0x9fe6): undefined reference to PyExc_SystemError'
:(.text+0x9ff5): undefined reference to PyErr_SetString' <artificial>:(.text+0xa050): undefined reference to PyErr_Restore'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11_object_dealloc': <artificial>:(.text+0xa934): undefined reference to PyObject_ClearWeakRefs'
:(.text+0xa93c): undefined reference to _PyObject_GetDictPtr' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::loader_life_support::add_patient(pybind11::handle)':
:(.text+0xaf3b): undefined reference to PyList_Append' <artificial>:(.text+0xaf56): undefined reference to PyList_New'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function bool pybind11::detail::type_caster_generic::load_impl<pybind11::detail::type_caster_generic>(pybind11::handle, bool)': <artificial>:(.text+0xb01f): undefined reference to _Py_NoneStruct'
:(.text+0xb03d): undefined reference to PyType_IsSubtype' <artificial>:(.text+0xb298): undefined reference to PyType_IsSubtype'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize<sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#8}, void, sofa::simulation::Node*, pybind11::name, pybind11::scope, pybind11::sibling>(sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#8}&&, void (*)(sofa::simulation::Node*), pybind11::name const&, pybind11::scope const&, pybind11::sibling const&)::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call)': <artificial>:(.text+0xb47a): undefined reference to _Py_NoneStruct'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize<sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#9}, void, sofa::simulation::Node*, pybind11::name, pybind11::scope, pybind11::sibling>(sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#9}&&, void (*)(sofa::simulation::Node*), pybind11::name const&, pybind11::scope const&, pybind11::sibling const&)::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call)': <artificial>:(.text+0xb4fa): undefined reference to _Py_NoneStruct'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize<sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#7}, void, sofa::simulation::Node*, pybind11::name, pybind11::scope, pybind11::sibling, char const*>(sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#7}&&, void (*)(sofa::simulation::Node*), pybind11::name const&, pybind11::scope const&, pybind11::sibling const&, char const* const&)::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call)': <artificial>:(.text+0xb5bd): undefined reference to _Py_NoneStruct'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize<sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#5}, void, sofa::simulation::Node*, pybind11::name, pybind11::scope, pybind11::sibling, char const*>(sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#5}&&, void (*)(sofa::simulation::Node*), pybind11::name const&, pybind11::scope const&, pybind11::sibling const&, char const* const&)::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call)': <artificial>:(.text+0xb65a): undefined reference to _Py_NoneStruct'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize<sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#4}, void, sofa::simulation::Node*, pybind11::name, pybind11::scope, pybind11::sibling>(sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#4}&&, void (*)(sofa::simulation::Node*), pybind11::name const&, pybind11::scope const&, pybind11::sibling const&)::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call)': <artificial>:(.text+0xb6ec): undefined reference to _Py_NoneStruct'
/tmp/ccWMVR0G.ltrans0.ltrans.o::(.text+0xb76a): more undefined references to _Py_NoneStruct' follow /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize<sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*, double)#2}, void, sofa::simulation::Node*, double, pybind11::name, pybind11::scope, pybind11::sibling, char const*>(sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*, double)#2}&&, void ()(sofa::simulation::Node, double), pybind11::name const&, pybind11::scope const&, pybind11::sibling const&, char const* const&)::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call)':
:(.text+0xb80b): undefined reference to PyFloat_Type' <artificial>:(.text+0xb815): undefined reference to PyType_IsSubtype'
:(.text+0xb821): undefined reference to PyFloat_AsDouble' <artificial>:(.text+0xb861): undefined reference to _Py_NoneStruct'
:(.text+0xb891): undefined reference to PyErr_Clear' <artificial>:(.text+0xb8b7): undefined reference to PyErr_Occurred'
:(.text+0xb8cd): undefined reference to PyExc_TypeError' <artificial>:(.text+0xb8d5): undefined reference to PyErr_ExceptionMatches'
:(.text+0xb8de): undefined reference to PyErr_Clear' <artificial>:(.text+0xb8eb): undefined reference to PyNumber_Check'
:(.text+0xb8f7): undefined reference to PyNumber_Float' <artificial>:(.text+0xb8ff): undefined reference to PyErr_Clear'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cpp_function::initialize<sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#1}, void, sofa::simulation::Node*, pybind11::name, pybind11::scope, pybind11::sibling, char const*>(sofapython3::pybind11_init_Simulation(pybind11::module&)::{lambda(sofa::simulation::Node*)#1}&&, void (*)(sofa::simulation::Node*), pybind11::name const&, pybind11::scope const&, pybind11::sibling const&, char const* const&)::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call)': <artificial>:(.text+0xb9ba): undefined reference to _Py_NoneStruct'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function PyInit_Simulation': <artificial>:(.text+0xba4b): undefined reference to Py_GetVersion'
:(.text+0xbae4): undefined reference to PyModule_Create2' <artificial>:(.text+0xbb4f): undefined reference to PyUnicode_DecodeUTF8'
:(.text+0xbb80): undefined reference to PyObject_SetAttrString' <artificial>:(.text+0xbb9f): undefined reference to _Py_NoneStruct'
:(.text+0xbbb4): undefined reference to PyObject_GetAttrString' <artificial>:(.text+0xbc82): undefined reference to PyObject_GetAttrString'
:(.text+0xbd4c): undefined reference to PyObject_GetAttrString' <artificial>:(.text+0xbe16): undefined reference to PyObject_GetAttrString'
:(.text+0xbed5): undefined reference to PyObject_GetAttrString' /tmp/ccWMVR0G.ltrans0.ltrans.o:<artificial>:(.text+0xbf9f): more undefined references to PyObject_GetAttrString' follow
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function PyInit_Simulation': <artificial>:(.text+0xc2d3): undefined reference to _Py_NoneStruct'
:(.text+0xc326): undefined reference to PyExc_ImportError' <artificial>:(.text+0xc340): undefined reference to PyErr_Format'
:(.text+0xc361): undefined reference to PyErr_Clear' <artificial>:(.text+0xc381): undefined reference to PyErr_Clear'
:(.text+0xc3a1): undefined reference to PyErr_Clear' <artificial>:(.text+0xc3c1): undefined reference to PyErr_Clear'
:(.text+0xc3e1): undefined reference to PyErr_Clear' /tmp/ccWMVR0G.ltrans0.ltrans.o:<artificial>:(.text+0xc401): more undefined references to PyErr_Clear' follow
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function PyInit_Simulation': <artificial>:(.text+0xca31): undefined reference to PyExc_ImportError'
:(.text+0xca39): undefined reference to PyErr_SetString' <artificial>:(.text+0xca65): undefined reference to PyExc_ImportError'
:(.text+0xca6d): undefined reference to PyErr_SetString' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::cast_error::set_error() const':
:(.text+0x149c): undefined reference to PyErr_SetString' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::instance::deallocate_layout()':
:(.text+0x23d5): undefined reference to PyMem_Free' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::module::add_object(char const*, pybind11::handle, bool) [clone .constprop.60]':
:(.text+0x299d): undefined reference to PyModule_AddObject' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::gil_scoped_acquire::~gil_scoped_acquire()':
:(.text+0x5b82): undefined reference to PyEval_SaveThread' /tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::detail::error_stringabi:cxx11':
:(.text.unlikely+0x144): undefined reference to PyErr_Occurred' <artificial>:(.text.unlikely+0x150): undefined reference to PyExc_RuntimeError'
:(.text.unlikely+0x15f): undefined reference to PyErr_SetString' <artificial>:(.text.unlikely+0x18d): undefined reference to PyErr_Fetch'
:(.text.unlikely+0x267): undefined reference to PyObject_Str' <artificial>:(.text.unlikely+0x2de): undefined reference to PyErr_NormalizeException'
:(.text.unlikely+0x301): undefined reference to PyException_SetTraceback' <artificial>:(.text.unlikely+0x353): undefined reference to PyFrame_GetLineNumber'
:(.text.unlikely+0x5ba): undefined reference to PyErr_Restore' <artificial>:(.text.unlikely+0x782): undefined reference to PyErr_Restore'
/tmp/ccWMVR0G.ltrans0.ltrans.o: In function pybind11::error_already_set::error_already_set()': <artificial>:(.text.unlikely+0x804): undefined reference to PyErr_Fetch'
collect2: error: ld returned 1 exit status
external_directories/plugin.SofaPython3/bindings/Sofa/src/SofaPython3/Sofa/Simulation/CMakeFiles/Bindings.Sofa.Simulation.dir/build.make:102: recipe for target 'lib/site-packages/Sofa/Simulation.cpython-37m-x86_64-linux-gnu.so' failed
make[2]: *** [lib/site-packages/Sofa/Simulation.cpython-37m-x86_64-linux-gnu.so] Error 1
CMakeFiles/Makefile2:11872: recipe for target 'external_directories/plugin.SofaPython3/bindings/Sofa/src/SofaPython3/Sofa/Simulation/CMakeFiles/Bindings.Sofa.Simulation.dir/all' failed
make[1]: *** [external_directories/plugin.SofaPython3/bindings/Sofa/src/SofaPython3/Sofa/Simulation/CMakeFiles/Bindings.Sofa.Simulation.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....

Missing "*Targets.cmake"files when building SofaPython3

Hello all,

I am trying to build sofa from sources (Windows 11), but am having some difficulties. When using the fetched SofaPython3 I get the following error:

Python path configuration:
  PYTHONHOME = (not set)
  PYTHONPATH = 'C:\Users\wilke\anaconda3'
  program name = 'python'
  isolated = 0
  environment = 1
  user site = 1
  import site = 1
  sys._base_executable = 'E:\\Github\\sofa\\build\\bin\\RelWithDebInfo\\runSofa.exe'
  sys.base_prefix = 'C:\\Users\\wilke\\anaconda3'
  sys.base_exec_prefix = 'C:\\Users\\wilke\\anaconda3'
  sys.platlibdir = 'lib'
  sys.executable = 'E:\\Github\\sofa\\build\\bin\\RelWithDebInfo\\runSofa.exe'
  sys.prefix = 'C:\\Users\\wilke\\anaconda3'
  sys.exec_prefix = 'C:\\Users\\wilke\\anaconda3'
  sys.path = [
    'C:\\Users\\wilke\\anaconda3',
    'C:\\Users\\wilke\\anaconda3\\python39.zip',
    'E:\\Github\\sofa\\build\\bin\\RelWithDebInfo',
  ]
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
Python runtime state: core initialized
ModuleNotFoundError: No module named 'encodings'

As a result I have tried an out of tree build of both the master and v22.06 branches of SofaPython3, here I am getting the following error with CMake (repeated for each component):

CMake Error at E:/Github/sofa/build/lib/cmake/Sofa.Component.Collision.Response.ContactConfig.cmake:39 (include):
  include could not find requested file:

    E:/Github/sofa/build/lib/cmake/Sofa.Component.Collision.Response.ContactTargets.cmake
Call Stack (most recent call first):
  E:/Github/sofa/build/lib/cmake/Sofa.GUI.CommonConfig.cmake:36 (find_package)
  E:/Github/sofa/build/lib/cmake/SofaGuiConfig.cmake:36 (find_package)
  bindings/SofaGui/CMakeLists.txt:16 (find_package)

Any help would be appreciated. Thank you in advance :)

Update:

The error with the fetched SofaPython3 was the result of conflicting Python versions detected during CMake configuration and in path and is now resolved.

Adding SofaPython3 plugin on Windows

I built Sofa on Windows (I took the master branch for the Sofa sources) and I tried to add the SofaPython3 plugin. When I tried via the Plugin Manager, this warning appeared on my terminal and Sofa crashed :

[WARNING] [PythonEnvironment] No python dir found in E:/sofa/build/bin

Indeed, the plugin is in the folder : E:/sofa/build/bin/Release and with the help of Damien, we saw that the problem came from the following function in PythonEnvironment.cpp :

void PythonEnvironment::addPythonModulePathsForPluginsByName(const std::string& pluginName)
{
    std::map<std::string, Plugin>& map = PluginManager::getInstance().getPluginMap();
    for( const auto& elem : map)
    {
        Plugin p = elem.second;
        if ( p.getModuleName() == pluginName )
        {
            std::string pluginLibraryPath = elem.first;
            // moduleRoot should be 2 levels above the library (plugin_name/lib/plugin_name.so)
            std::string moduleRoot = FileSystem::getParentDirectory(FileSystem::getParentDirectory(pluginLibraryPath));
            addPythonModulePathsForPlugins(moduleRoot);
            return;
        }
    }
    msg_warning("PythonEnvironment") << pluginName << " not found in PluginManager's map.";
}

It seems logical to have this warning because the function uses twice getParentDirectory and as a consequence, from E:/sofa/build/bin/Release/SofaPython3.dll we go to E:/sofa/build/bin, where there is no python3 dir.

We changed the source code and more precisely the file PythonEnvironment.cpp by adding a third getParentDirectory. Thus the line is now :

std::string moduleRoot = FileSystem::getParentDirectory(FileSystem::getParentDirectory(FileSystem::getParentDirectory(pluginLibraryPath)));

With that in mind, we added a PYTHONPATH like this : E:/sofa/build/python3/site-packages. With those changes, it seems to work but my question is : is this an error from the source code to not be able to add the plugin from the plugin manager, or is this more a problem with CMake with a Windows distribution that put some files or folders in a place where it should not be during the compilation?

Which version of SOFA does this need?

I am trying to compile this plugin using SOFA master (6b53f37fa6) and with SOFA v19.06 but it always gives an error related to DAGNode.

Which version of SOFA does this need?

Documentation about how sofa::Data is bound

I have a lot of doubts about how sofa::Data is bound to SofaPython3. Apparently, when sofa::Data contains a VecCoord, is it converted to a numpy array, which i can access with I can access and modify with [], but I can't iterate. For other types, its not clear how is bound (for example with a filepath).

Is there any documentation on how sofa::Data<> is bound to sofa, and how it affects regarding read/write access in C++ side?

getLinkPath not working

Exactly what the title says. Sorry I have no clue...

[ERROR]   [SofaPython3::SceneLoader] KeyError: 'getLinkPath'

SofaPython3 out-of-tree-build errors

Hi,

I am trying to add the sofapython3 to the sofa (version 20.12.2) using the given direction. during the cmake -DCMAKE_PREFIX_PATH=$SOFA_ROOT/build/install/lib/cmake step, it gives a cmake error as it cannot find SofaSimulationGraphConfig.cmake and Sofa.Testing.cmake.

I was wondering if I am missing anything or is it a bug.

Many thanks
Amir

Problem In building the plugin

building in-tree the plugin of SofaPython3 is giving me problems: in the compiling with ninja the compiler stops at the following commandst:
[68/114] Building CXX object external_...s.Sofa.Helper.dir/Binding_Vector.cpp.o
[68/115] Building CXX object external_...ngs.Sofa.Core.dir/Binding_Prefab.cpp.o
do you know why this happens?

Run a simulation directly from SOFA

I am trying to run a sofa simulation directly from a python script, not running runSofa and loading a python scene but directly a python script. I tried something similar to what is inside runFromPython.py file.

Python 3.8.6 (default, Sep 30 2020, 04:00:38)
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import SofaRuntime
>>> SofaRuntime.PluginRepository.addLastPath('/usr/local/lib')
>>> SofaRuntime.PluginRepository.addLastPath('/usr/local/plugins')
>>> SofaRuntime.importPlugin('SofaHaptics')

After debugging a bit, Ive found that the problem is that SofaHaptics needs SofaConstraint, but it can't find it.

[ERROR]   [PluginManager] Plugin loading failed (/usr/local/plugins/SofaHaptics/lib/libSofaHaptics_d.so): libSofaConstraint_d.so.20.06.99: cannot open shared object file: No such file or directory

That library exists is /usr/local/lib, which is where SOFA is installed.

Any ideas on how to make this work?

CRASH when running sofa in python3 interpreter

Sofa is now crashing when used in a python3 interpreter.

Because some python3 PythonEnvironment method are used while python environment is not initialized.
I'm not sure what should be the proper fix (probably not using PythonEnvironment in the bindings ... but it is a big change so for v22.06 a quick fix should be welcome.

At first sight, my guess is that maybe python environment should be split in two PythonInterpreter and PythonEnvironment.

Sofapython3 plugin loading

Hi, I have downloaded the latest version of sofa using binaries but when I try to load the plugin .dll file sofa says that it cannot find such file. I have tried using different python version and also different sofa ones but with the same result.
Can someone help me?

Get MechanicalObject from Python to C++

I want to add some free function to retrieve the state from a MechanicalObject in Python. I need to do it this way because my MechanicalObject is templated using a type that is not convertible to Python when wrapped on a sofa::Data (eigen matrix), so my idea is to add a free function which receives the MechanicalObject and extract the position from the data and returns a vector with it.

Sadly, I am not sure which type of object my free function should receive. I have tried MechanicalObject but that doesn't work. Also tried to receive a py::handle, but can't cast it to MechanicalObject. Any idea on how to proceed?

Required plugins

We would like to generalize the addition of a required plugin from a Prefab.

One idea was to add a predefined property, like parent for instance, that would be processed like this:

  • display the given list of plugins in the 'info' tab of the prefab (node)
  • load the plugins if necessary

Now that I think about it, I'm wondering if it shouldn't be generalized to a Node...

node = addChild('Visu')
node.requiredPlugins = 'SofaOpenglVisual SofaLoader'

With the pluginization(?) of Sofa I think it would be a great feature.

Shipping SofaPython3 as a plugin and PYTHONPATH

Linked with the discussion #226
User has to define explicitly the path of SofaPython3 python package into the env.var PYTHONPATH.
IMO it is not really straight-forward, and should be done automatically if possible.

Build errors

Hi,
I'm building the plugin on Ubuntu 18.04 with python3.7.9, following this link: https://sofapython3.readthedocs.io/en/latest/menu/install.html.
When running make, I get the following error:
(1) serveral ones about getPathName():

[12/72] Building CXX object external_directories/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeFiles/Bindings.Sofa.Core.dir/Data/Binding_DataVectorString.cpp.o
FAILED: external_directories/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeFiles/Bindings.Sofa.Core.dir/Data/Binding_DataVectorString.cpp.o 
/usr/bin/g++ -DBindings_Sofa_Core_EXPORTS -DNDEBUG -DSOFA_HAVE_SOFAPYTHON3 -I/home/beichun/research/soft-gripper/sofa/src/SofaPython3/bindings/Sofa/src -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaCore/src -Iinclude/SofaFramework -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaHelper/src -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaHelper/../SofaSimulationCore/src -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaHelper/../SofaDefaultType/src -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaHelper/../SofaCore/src -Iinclude/GTest -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaDefaultType/src -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaSimulationCore/src -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaSimulationGraph/.. -Iinclude/SofaSimulation -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaSimulationCommon/.. -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaBaseVisual/.. -Iinclude/SofaBase -I/home/beichun/research/soft-gripper/sofa/src/SofaKernel/modules/SofaBaseTopology/.. -I/home/beichun/research/soft-gripper/sofa/src/SofaPython3/Plugin/src -isystem /usr/include/python3.7m -isystem /home/beichun/.env/sofa/lib/python3.7/site-packages/pybind11/include -isystem /home/beichun/research/soft-gripper/sofa/src/SofaKernel/extlibs/json -isystem /home/beichun/research/soft-gripper/sofa/src/extlibs/gtest/include -isystem /usr/include/eigen3 -Wall -W -Wno-padded -O3 -DNDEBUG -fstack-protector --param=ssp-buffer-size=4 -D_FORTIFY_SOURCE=2 -fPIC -fvisibility=hidden -Dregister= -flto -fno-fat-lto-objects -DFRAMEWORK_TEST_RESOURCES_DIR=\"/home/beichun/research/soft-gripper/sofa/src/SofaKernel/SofaFramework/resources/tests\" -pthread -DGTEST_LINKED_AS_SHARED_LIBRARY=1 -std=c++1z -MD -MT external_directories/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeFiles/Bindings.Sofa.Core.dir/Data/Binding_DataVectorString.cpp.o -MF external_directories/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeFiles/Bindings.Sofa.Core.dir/Data/Binding_DataVectorString.cpp.o.d -o external_directories/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeFiles/Bindings.Sofa.Core.dir/Data/Binding_DataVectorString.cpp.o -c /home/beichun/research/soft-gripper/sofa/src/SofaPython3/bindings/Sofa/src/SofaPython3/Sofa/Core/Data/Binding_DataVectorString.cpp
In file included from /home/beichun/research/soft-gripper/sofa/src/SofaPython3/bindings/Sofa/src/SofaPython3/Sofa/Core/Data/Binding_DataVectorString.cpp:40:0:
/home/beichun/research/soft-gripper/sofa/src/SofaPython3/Plugin/src/SofaPython3/DataHelper.h: In member function ‘bool sofa::core::objectmodel::PrefabLink::operator==(const sofa::core::objectmodel::PrefabLink&) const’:
/home/beichun/research/soft-gripper/sofa/src/SofaPython3/Plugin/src/SofaPython3/DataHelper.h:94:53: error: ‘class sofa::core::objectmodel::Base’ has no member named ‘getPathName’; did you mean ‘getTypeName’?
                             return getTargetBase()->getPathName() == value.getTargetPath();
                                                     ^~~~~~~~~~~
                                                     getTypeName
/home/beichun/research/soft-gripper/sofa/src/SofaPython3/Plugin/src/SofaPython3/DataHelper.h:99:78: error: ‘class sofa::core::objectmodel::Base’ has no member named ‘getPathName’; did you mean ‘getTypeName’?
                             return getTargetPath() == value.getTargetBase()->getPathName();
                                                                              ^~~~~~~~~~~
                                                                              getTypeName

(2) d_componentState:

/home/beichun/research/soft-gripper/sofa/src/SofaPython3/Plugin/src/SofaPython3/Prefab.cpp: In member function ‘virtual void sofapython3::Prefab::doReInit()’:
/home/beichun/research/soft-gripper/sofa/src/SofaPython3/Plugin/src/SofaPython3/Prefab.cpp:56:5: error: ‘d_componentState’ was not declared in this scope
     d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid);
     ^~~~~~~~~~~~~~~~
/home/beichun/research/soft-gripper/sofa/src/SofaPython3/Plugin/src/SofaPython3/Prefab.cpp:56:5: note: suggested alternative: ‘d_componentstate’
     d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid);
     ^~~~~~~~~~~~~~~~
     d_componentstate

(3) setLinkedBase:

/home/beichun/research/soft-gripper/sofa/src/SofaPython3/Plugin/src/SofaPython3/DataHelper.cpp: In function ‘sofa::core::objectmodel::BaseLink* sofapython3::addLink(pybind11::object, const string&, pybind11::object, const string&)’:
/home/beichun/research/soft-gripper/sofa/src/SofaPython3/Plugin/src/SofaPython3/DataHelper.cpp:622:15: error: ‘class sofa::core::objectmodel::BaseLink’ has no member named ‘setLinkedBase’; did you mean ‘getLinkedBase’?
         link->setLinkedBase(py::cast<Base*>(value));
               ^~~~~~~~~~~~~
               getLinkedBase

By the way, I was able to build sofa without problems.
Any help is appreciated!

Test failing XMLParser

A test is failing on Windows (and was but we had no CI before to detect it) :
[XMLParser] Failed to open C:\Users\RUNNER~1\AppData\Local\Temp\tmp9e5sp9oz.scn

Let's fix this!

Identified in PR #213

Add a GHA testing the execution of a scene in an interpreter

Further to #284 we noticed that missing tests on the execution of a SOFA simulation in a python interpreter

For the future, refactoring the PythonEnvironment file to split PythonEnvironment in two, to separate what is actually relevant for the PythonEnvironment and what should be part something like PythonEmbededInterpreter.

Raise a warning or an error when a property is not provided with the correct dimension

This issue is linked to this discussion in the SoftRobots Plugin.

When defining a Prefab object, zero(s) will be concatenated to the end if a property of vector type is provided with a shorter vector. @EulalieCoevoet provided a good example:

import Sofa

class EmptyPrefab(Sofa.Prefab):

    properties = [
        {'name':'myProperty', 'type':'Vec4d', 'help':'',  'default':[1.,1.,1.,1.]}
    ]

    def __init__(self, *args, **kwargs):
        Sofa.Prefab.__init__(self, *args, **kwargs)
        print(self.myProperty.value)


def createScene(root):
    myPrefab = EmptyPrefab(myProperty=[1,1])
    root.addChild(myPrefab)

When running the above code, [1. 1. 0. 0.] will be printed. Although being a good and safe default behavior, this behavior can lead to confusion in some cases. For example, when creating a OglModel with
node.addObject('OglModel', src="@mesh", color="0.8 0.7 0.7")
the color property will be appended with a 0.0 to make it 4D, which makes the OglModel transparent, and misleads users to think that the Visual Models are not working properly.

So it could be beneficial to raise a warning when a property value is not provided with the correct dimension.

What is the expected behavior when passing a Data as argument in the object behavior ?

What should be the behavior when passing other data field as argument during object creation.

Example:

def createScene(root):
    root.addObject("MeshObjLoader", filename="mesh/cube.obj", name="loader");
    a = root.addObject("PointSetTopologyContainer", position=root.loader.position, name="topo") 
    b = root.addObject("PointSetTopologyContainer", position=root.loader.position.value, name="topo") 

To me a should make a link, while b should copy the value. If you share this few then ... the current implementation need to be fixed

fail to make

Hi, Hugo.
I have been compiling SP3 using out of tree build, despite some Eigen warning,cmake was perfectly fine.
However,got some problem like this when execute cmake --build $SP3_build :

code/SofaPython3-master/Plugin/src/SofaPython3/initModule.cpp:21:0: /home/SENSETIME/xulixin2/code/SofaPython3-master/Plugin/src/SofaPython3/config.h:48:5: error: user-defined literal in preprocessor expression #if PYBIND11_SOFA_VERSION >= 20801 ^ In file included from /usr/local/include/pybind11/cast.h:13:0, from /usr/local/include/pybind11/attr.h:13, from /usr/local/include/pybind11/detail/class.h:12, from /usr/local/include/pybind11/pybind11.h:13, from /home/SENSETIME/xulixin2/code/SofaPython3-master/Plugin/src/SofaPython3/config.h:24, from /home/SENSETIME/xulixin2/code/SofaPython3-master/Plugin/src/SofaPython3/initModule.cpp:21: /home/SENSETIME/xulixin2/code/SofaPython3-master/Plugin/src/SofaPython3/config.h:45:7: error: floating constant in preprocessor expression + PYBIND11_VERSION_PATCH) ^ /home/SENSETIME/xulixin2/code/SofaPython3-master/Plugin/src/SofaPython3/config.h:48:5: note: in expansion of macro ‘PYBIND11_SOFA_VERSION’ #if PYBIND11_SOFA_VERSION >= 20801
I don't know how to deal with it, appreciate it if you could give some advice.

Test the new feature linkpath on objects

A new feature on all SOFA objects object.linkpath now returns a type LinkPath (not a string) to provide directly path of objects in the scene and connect components. This feature is being tested and either keep as is, replaced (a string used as object.path) or removed.

Associated PR : #265

TetrahedronDiffusionFEMForcefield errors on construction in python3

Hi, me again. I'm hoping that I'm doing something wrong, but it seems like there is an issue on construction of the TetrahedronDiffusionFEMForcefield. With the following bare minimum script:

def createScene(rootNode):
    rootNode.addObject('MeshVTKLoader', name='meshLoader', filename='mesh/raptorTetra_8418.vtu')
    rootNode.addObject('MeshObjLoader', name='potentialLoader', filename='mesh/raptorTemperature.obj')
    rootNode.addObject('MechanicalObject', name='tetradofs', template='Vec3d')
    rootNode.addObject('TetrahedronSetTopologyContainer', src='@meshLoader', name='asdf')
    temperatures = Sofa.Components.MechanicalObject(rootNode, template='Vec1d', name='gridTemperature', position='@potentialLoader.position', tags='heat')
    diffusion = Sofa.Components.TetrahedronDiffusionFEMForceField(rootNode,
                                                                  template='Vec1d',
                                                                  topology='@asdf',
                                                                  name='DiffusionForceField',
                                                                  constantDiffusionCoefficient=1500,
                                                                  tagMechanics="mechanics")

I always get the error:
[ERROR] [TetrahedronDiffusionFEMForceField(TetrahedronDiffusionFEMForceField)] cannot find the mechanical object named '0
Seems like the wrong value for the name of the mechanical state is getting passed somewhere?

Can't expose properties of objects deriving from BaseObject

I am creating my own custom types for SOFA, mainly some forcefields but also other stuff. In order to add those types to an object, they must inherit from Sofa.Core.Object on python. This is problematic, since Sofa.Core.Base overrides how properties are read and written. In the end, this breaks adding new properties to custom defined types that are exposed to python.

    using Inherit = sofa::core::objectmodel::BaseObject;
    using CG = VNCS::ConjugateGradientSolver;

    ::py::class_<CG, Inherit, sofa::core::sptr<CG>>(m, "ConjugateGradient")
        .def(::py::init([]() { return sofa::core::objectmodel::New<CG>(); }))
        .def_property(
            "iterations",
            [](const CG &cg) { return cg.iterations(); },
            [](CG &cg, int iterations) { cg.setIterations(iterations); })
        .def("setIterations", [](CG &cg, int iterations) { cg.setIterations(iterations); })
        .def_property("tolerance", &CG::toleranceThreshold, &CG::setToleranceThreshold);

With that class exposed to python, it is not possible to do

cgSolver = myModule.ConjugateGradient
cgSolver.iterations = 50 # Doesn't work
print(cgSolver.iterations) # prints 25, the default value in the C++ class

This is the output for some tests

cgSolver = PyVNCS.ConjugateGradient()
dir(cgSolver)
['bbox', 'componentState', 'listening', 'name', 'printLog', 'tags']
cgSolver.iterations = 60
cgSolver.iterations
25
cgSolver.setIterations(40)
None
cgSolver.iterations
40
getattr(cgSolver, 'iterations')
40
setattr(cgSolver, 'iterations', 60)
None
getattr(cgSolver, 'iterations')
40

If ConjugateGradient doesn't inherit from Sofa.Core.Object, then the properties work fine, but I can't add it to a node.

Hardcoded path to pybind11 in Plugin target

When linking the Plugin cmake target from an installed SofaPython3 (i.e. downloaded zip on SOFA website) to our cmake target (for ex, caribou), the following error happen:

CMake Error in src/SofaCaribou/Python/CMakeLists.txt:
  Imported target "SofaPython3::Bindings.Sofa.Core" includes non-existent
  path

    "C:/pybind11/2.4.3/install/include"

  in its INTERFACE_INCLUDE_DIRECTORIES.  Possible reasons include:

  * The path was deleted, renamed, or moved to another location.

  * An install or uninstall procedure did not complete successfully.

  * The installation package was faulty and references files it does not
  provide.

Indeed, looking into the installed cmake files of SofaPython3, we find this in lib/cmake/SofaPython3/PluginTargets.cmake:

set_target_properties(SofaPython3::Plugin PROPERTIES
  INTERFACE_COMPILE_DEFINITIONS "HAVE_SNPRINTF"
  INTERFACE_INCLUDE_DIRECTORIES "C:/pybind11/2.4.3/install/include;${_IMPORT_PREFIX}/include"
  INTERFACE_LINK_LIBRARIES "Sofa.Core;Sofa.DefaultType;Sofa.SimulationCore;SofaSimulationGraph;Sofa.Helper;pybind11::module;pybind11::embed"
)

which uses an hardcoded path to pybind11 v2.4.3. I suppose this is where pybind11 was installed on the machine that created the SOFA package.

Add bindings for sofa::component::interactionforcefield::LinearSpring

This issue was raised by @ScheiklP (see here).

The data field SpringForceField::springs has the following type:

sofa::helper::vector<sofa::component::interactionforcefield::LinearSpring<Real>>

Since the class sofa::component::interactionforcefield::LinearSpring hasn't been bound to python, getting the datafield in python returns a vector of empty entries (the shape of the vector is good since sofa::helper::vector has been bound to python).

To fix it, we need to add bindings for sofa::component::interactionforcefield::LinearSpring<Real>. These will need to be added in a new binding library. Following what we have done for SofaBaseTopology, the following steps would be needed:

  1. Add a new CMake project Bindings.Modules.SofaDeformable in bindings/Modules/src/SofaPython3/SofaDeformable
  2. In this project, create a new python module SofaDeformable and link it against the SOFA target SofaDeformable
  3. Add bindings for the classes:
    1. sofa::component::interactionforcefield::SpringForceField, especially the methods addSpring and removeSpring
    2. sofa::component::interactionforcefield::LinearSpring
  4. Add unittests for these new bindings (we can base the tests on the following scene provided by @ScheiklP)

Memory leak when unloading a graph

Hi @hugtalbot @damienmarchal ,
I discovered a somewhat mysterious memory leak.

Steps to reproduce:

  1. load a simulation with MechanicalObjects
  2. get a writeable view on the position with mechanical_object.position.writeable() as pos: ...
  3. unload the simulation

On each loop, memory increases.

import Sofa
import Sofa.Core
import Sofa.Simulation
from typing import Optional


class SimulationHandler:
    def __init__(self):

        self.root_node = Sofa.Core.Node("root")
        self.objects = createScene(self.root_node, None)
        Sofa.Simulation.init(self.root_node)

    def step(self, with_change: bool = False):

        if with_change:
            for obj in self.objects:
                with obj.position.writeable() as pos:
                    pass

        Sofa.Simulation.animate(self.root_node, self.root_node.getDt())

    def reload(self, object_name: Optional[str] = None):

        Sofa.Simulation.unload(self.root_node)
        self.root_node = Sofa.Core.Node("root")
        self.objects = createScene(self.root_node, object_name)
        Sofa.Simulation.init(self.root_node)


def createScene(root, object_name):
    root.addObject("DefaultAnimationLoop")
    objects = []
    for i in range(20):
        child = root.addChild(f"child_{i}")
        if object_name is not None:
            obj = child.addObject(object_name, position=[0, 0, 0] * 50)
            objects.append(obj)

    return objects


if __name__ == "__main__":
    simulation = SimulationHandler()

    for _ in range(500):
        simulation.step(with_change=False)
        simulation.reload(object_name="MechanicalObject")

    for _ in range(500):
        simulation.step(with_change=True)
        simulation.reload(object_name="MechanicalObject")

image

Cheers,
Paul

runSofa crashes when creating a Sofa.Core.Controller object

I've sucessfully compiled the latest sofa with the lastest SofaPython3 without errors on Windows 10 with Visual Studio 16. However, when I try and run the example "ControllerScene.py" with runSofa it crashes when calling the constructor for the base class in the init function. I've tried just simply creating a default Sofa.Core.Controller() object and it also crashes. Running the script directly with python does work without any problems because it doesn't need to create a Controller.

Am I possibly missing a plugin that the Controller needs?
The only thing I see from the output in terminal is a warning:
No python dir found in C:/Users/Somers/git/sofa-latest/build/install

I know the Python3 version is still heavily in development, but is this an issue that could be easily solved? What additional info would be helpful to help figure out the problem?

runSofa.exe -l SofaPython3 crashes on Windows

As stated in the title, when trying to launch runSofa compiled with MSVC, with SofaPython3, the application crashes, with the output:

[INFO]    [SofaPython3]  Initializing with python version 3.9.0 (tags/v3.9.0:9cf6752, Oct  5 2020, 15:34:40) [MSC v.1927 64 bit (AMD64)]
[INFO]    [SofaPython3] Registering a scene loader for [.py, .py3, .pyscn, .py3scn] files.
[INFO]    [SofaPython3] Intializing python
[INFO]    [SofaPython3] Added 'D:/dev/sofa-github/extPlugins/SoftRobots/python' to sys.path
[INFO]    [SofaPython3] Added 'D:/dev/sofa-github/bd/python3/site-packages' to sys.path
[INFO]    [SofaPython3] Added 'D:/dev/sofa-github/bd/lib/python3/site-packages' to sys.path

Ultimately, what happens is that pybind throws an exception (pybind11::error_already_set) when reaching line 232 of PythonEnvironment.cpp:
py::module::import("SofaRuntime");

Apparently, the issue comes from the fact that 2 different 'site-packages' folders are created during build:

  • bd/python3/site-packages, which contains the .pyd files
  • bd/lib/python3/site-packages, which contains the 'init.py' files
    Copying manually the contents of one site-packages folder to the other, and suppressing the first one is a quick hack that works for me.
    Thanks @fredroy for the help 👍

NB: At the moment, this issue occurs after using another hack to workaround #159 which also happens on Windows

Creating Symlink to Python Package Directory creates Broken Links when installing

I'm using Sofa 21.06 with SofaPython3 on a machine with the following Configuration:

  • Ubuntu 20.04
  • Python 3.8 (Virtualenv)
  • Cmake 3.16.3

When I am setting the "SP3_PYTHON_PACKAGES_LINK_DIRECTORY" Variable to the site-packages folder of my Virtualenv and set the "SP3_LINK_TO_USER_SITE" variable, only broken links are created in the "SP3_PYTHON_PACKAGES_LINK_DIRECTORY".

Therefore I cannot import Sofa in this Python Env or any other Python.

I already have a fix and will post it in a few Minutes.

isDirty returning always false with writeable access

Originally posted by @ScheiklP in #290 (comment)


When trying to write into MechanicalObject.position, with writeable() or __setitem__, SofaPython3 checks if
the data is dirty or not in memcache.
For some reason, the d->isDirty() check always evaluates to false.
So after calling the function the first time, d is in memcache and the not-updated version of the array is used.
This is why len(MechanicalObject.position.writeable() is incorrect.

https://github.com/sofa-framework/SofaPython3/blob/master/Plugin/src/SofaPython3/DataHelper.cpp#L430-L445

py::array getPythonArrayFor(BaseData* d)
{
    auto& memcache = getObjectCache();
    if(d->isDirty() || memcache.find(d) == memcache.end())
    {
        auto capsule = py::capsule(new Base::SPtr(d->getOwner()), [](void*p){ delete static_cast<Base::SPtr*>(p); } );

        py::buffer_info ninfo = toBufferInfo(*d);
        py::array a(pybind11::dtype(ninfo), ninfo.shape,
                    ninfo.strides, ninfo.ptr, capsule);

        memcache[d] = a;
        return a;
    }
    return memcache[d];
}

Since the binding itself is functional, I would like to merge it, and then open another issue to address the actual problem.

  • Long term, by making sure that d->isDirty() correctly evaluates to true.
  • Short term, by checking if the shapes of d and memcache[d] are the same.

@damienmarchal @epernod

Handling python exceptions

The discussion started in #92 .

The following scene will behave differently either we are using SofaPython(2) or SofaPython3:

Test scene

# test.py
def createScene(root)
    root = '2' + 2

Using SofaPython(2):

[ERROR]   [PythonScript] TypeError: cannot concatenate 'str' and 'int' objects
  File "test.py", line 2, in createScene
    root = '2' + 2

Using SofaPython3:

[ERROR]   [SofaPython3::SceneLoader] TypeError: can only concatenate str (not "int") to str

At:
  test.py(2): createScene

In order to have the same behavior, we would have to place an exception hook in python interpreter of the plugin SofaPython3. However, this was not possible before python 3.8:

@damienmarchal in #92
I think there is two issue mixed together.
One is about the exception risen from python. In SofaPython they were catched by a dedicated hook installed by Sofa in the python interpreter. Because of multithreading this feature was broken before 3.8 and thus SofPython3 was not using it. The consequence is that when there is an error at the python level the behavior is different.

OglModel causes GUIManager.createGUI to fail

I hate to be the one to bring another issue to the table, but when I run the following code in runSofa, it works fine, but when I run it directly in python, it crashes with the error below. When I comment out the visual = ... line it does not crash. Any thoughts? Am I using the GUIManager correctly? I am running on a latest masterbranch compilation of sofa with Ubuntu 18.4 (on Windows wsl).

from SofaRuntime import PluginRepository
import Sofa
import Sofa.Gui
import sys, os


def createScene(root):
    root.addObject("RequiredPlugin", name="SofaOpenglVisual")
    obj = root.addChild("obj")
    meshLoader = obj.addObject('MeshObjLoader', name="loader", filename="mesh/Armadillo_simplified.obj", scale=10)
    visual = obj.addObject('OglModel', name="visual", src="@loader")
    return root


if __name__ == '__main__':
    print("Supported GUIs are " + Sofa.Gui.GUIManager.ListSupportedGUI(","))
    PluginRepository.addFirstPath(os.path.split(os.path.split(sys.executable)[0])[0] + "/plugins")

    root = Sofa.Core.Node("root")
    createScene(root)

    # Initialize all components found in the scene
    Sofa.Simulation.init(root)

    # Launch the GUI
    Sofa.Gui.GUIManager.Init("qt")
    Sofa.Gui.GUIManager.createGUI(root)
    Sofa.Gui.GUIManager.MainLoop()
    Sofa.Gui.GUIManager.closeGUI()

Error:

[ERROR]   [SofaRuntime] RuntimeError: basic_string::_M_construct null not valid
Traceback (most recent call last):
  File "/usr/lib/python3.7/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 1, in <module>
  File "/home/psomers/pycharm-community-2020.2.1/plugins/python-ce/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile
    pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script
  File "/home/psomers/pycharm-community-2020.2.1/plugins/python-ce/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/home/psomers/git/moveable_view/ROI_extraction.py", line 28, in <module>
    Sofa.Gui.GUIManager.createGUI(root)

Release v20.12

Remaining tasks for v20.12:

  • Fix all tests in Windows MSVC
  • Create a Windows MSVC github workflow
  • Clean-up all example scenes file and make sure they work on all supported OS
  • Fix the readthedocs automatic builds
  • Improve the documentation
  • Use SOFA CMake macros for package, modules and targets (see sofa-framework/sofa#1671)
  • Create a release workflow and deploy python binaries alongside the plugin
  • Test the release binaries on clean VMs with the installed beta release of SOFA
  • Create v20.12 tag

Scene creation stops when encounter a warning

When a warning is raised by Sofa, the creation of the scene stops.

After a discussion with Damien, it seems that SofaPython3 should catch python3's exceptions, the same way it was done in SofaPython plugin.

If I understood correctly it wasn't possible with python 3.8 but it should be with the new release 3.9.

Sofa.Gui crashes when importing into a runSofa executable (embed interpreted)

For some reason, this only happen when using a packaged version of SOFA (i.e. not build install by ourself).

To reproduce:

  1. Download the latest SOFA v20.12 package
  2. Create this simple scene file and do not put it inside the directory from which runSofa will run, but use instead a different folder:
    test/test.py
# test/test.py
import Sofa.Gui
def createScene(root):
    pass
  1. Run it using runSofa, for example: runSofa -l libSofaPython3.so test/test.py
  2. This happens:
[ERROR]   [SofaPython3::SceneLoader] ModuleNotFoundError: No module named 'Sofa.Gui'

At:
  test.py(1): <module>
  <frozen importlib._bootstrap>(219): _call_with_frames_removed
  <frozen importlib._bootstrap_external>(728): exec_module
  <frozen importlib._bootstrap>(677): _load_unlocked
  <frozen importlib._bootstrap>(696): _load
  /usr/lib/python3.7/imp.py(171): load_source
  /usr/lib/python3.7/imp.py(234): load_module
  <string>(3): <module>

Moreover, everything is fine (i.e. the bug isn't present) if runSofa is ran from the same directory as the scene file. For example, running runSofa -l libSofaPython3.so test.py instead of runSofa -l libSofaPython3.so test/test.py

Memory corruption with some data copy pattern

During our weekly coding sprint it was reported that this kind of pattern is failing:

with rootNode.mechanicalObject.position.writableArray() as w:
             w[0] = rootNode.otherObject.position.value[0]

Should be investigated quickly.

SofaPython3 DataEngine Update Function's Problem

There is problem about the data-update.

When the ./runsofa is running, the demo named emptydataengine.py cannot update data such as many msg_info. It can run the init() function, however, the update() function can not output any info.

`class EmptyDataEngine(Sofa.Core.DataEngine):

def __init__(self, *args, **kwargs):                                                                    
    Sofa.Core.DataEngine.__init__(self, *args, **kwargs)                                                
    pass                                                                                                
                                                                                                        
def init(self):                                                                                         
    pass                                                                                                
                                                                                                        
def update():                                                                                           
    # Function called anytime an output is accessed while the component                                 
    # is dirty (input has changed) (It means this function can not be used????)                                                                     
    msg_info('Not implemented yet')  ` 

Compilation issue

I am getting an error when doing "out-of-tree" build of the plugin v21.06 using clang-12 on ubuntu20.04:

SofaPython3/Plugin/src/SofaPython3/PythonEnvironment.h:93:26: error: no member named 'logging' in namespace 'sofa::helper'
    static sofa::helper::logging::FileInfo::SPtr getPythonCallingPointAsFileInfo() ;

Is this a known issue?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.