CadQuery / cadquery

A python parametric CAD scripting framework based on OCCT
https://cadquery.readthedocs.io
Other
3.25k stars 294 forks source link

Crash when trying to call `assembly.solve()`: "Plugin 'ipopt' is not found" [when running from the cadquery-server docker image] #1181

Open iansan5653 opened 2 years ago

iansan5653 commented 2 years ago

👋 Hello! I'm new to CadQuery and still learning, so it's very possible this is due to user error and not a bug. I'm assuming I just need to install something, though I am not sure what to install / why it wasn't included in the Docker container I'm using.

I am usually able to build models, sketches, assemblies, etc without any issues. But whenever I try to call solve() on an assembly instance, my part fails.

To Reproduce

It appears that any assembly will trigger this, no matter how complex. Here's a sample:

import cadquery as cq

def cone():
  result: cq.Workplane = cq.Workplane("XY").add(cq.Solid.makeCone(1, 0, 2))
  result.faces("<Z").tag("bottom")
  return result

assy = cq.Assembly()
assy.add(cone(), name="cone1")
assy.add(cone(), name="cone2")

assy.constrain("cone1?bottom", "cone2?bottom", "Point").solve()

Backtrace

CasADi - 2022-10-16 22:45:12 WARNING(".../casadi/core/plugin_interface.hpp:322: Assertion "handle!=nullptr" failed:
PluginInterface::load_plugin: Cannot load shared library 'libcasadi_nlpsol_ipopt.so': 
   (
    Searched directories: 1. casadipath from GlobalOptions
                          2. CASADIPATH env var
                          3. PATH env var (Windows)
                          4. LD_LIBRARY_PATH env var (Linux)
                          5. DYLD_LIBRARY_PATH env var (osx)
    A library may be 'not found' even if the file exists:
          * library is not compatible (different compiler/bitness)
          * the dependencies are not found
   )
  Tried '/usr/local/lib/python3.10/site-packages/casadi' :
    Error code: libgfortran.so.5: cannot open shared object file: No such file or directory
  Tried '' :
    Error code: libgfortran.so.5: cannot open shared object file: No such file or directory
  Tried '.' :
    Error code: ./libcasadi_nlpsol_ipopt.so: cannot open shared object file: No such file or directory") [.../casadi/core/plugin_interface.hpp:171]
Traceback (most recent call last):
  File "/workspaces/cadquery-template/box.py", line 41, in <module>
    assy.solve()
  File "/usr/local/lib/python3.10/site-packages/cadquery/assembly.py", line 417, in solve
    locs_new, self._solve_result = solver.solve()
  File "/usr/local/lib/python3.10/site-packages/cadquery/occ_impl/solver.py", line 717, in solve
    sol = opti.solve_limited()
  File "/usr/local/lib/python3.10/site-packages/casadi/casadi.py", line 27621, in solve_limited
    return _casadi.Opti_solve_limited(self, *args)
RuntimeError: Error in Opti::solve [OptiNode] at .../casadi/core/optistack.cpp:167:
.../casadi/core/plugin_interface.hpp:417: Plugin 'ipopt' is not found.

Environment

I am working in an environment built from the cadquery/cadquery-server Docker image. More specifically, I am running CadQuery in a GitHub Codespaces remote environment (created from this template).

lorenzncode commented 2 years ago

Hi! I tested your script with a conda installation and could not reproduce the issue.

I did not try the docker/cadquery-server installation method as I don't have experience with it yet. FYI it is a separate project announced in #1147. The project link can be found there. You might open an issue there as this looks to be specific to cadquery-server.

Are you able to run JupyterLab in Codespaces? If so, you could try installing CadQuery with conda or pip then run JupyterLab.

CadQuery has native support for basic display of objects and assemblies in Jupyter. Call display rather than show_object.

Screenshot from 2022-10-17 21-35-41

adam-urbanczyk commented 2 years ago

Same here, it works for my with conda. The image is not provided by this repo (not even by this org AFAICT), so pls open an issue with the author of the docker image.

iansan5653 commented 2 years ago

Thanks. The Docker image is hosted by the cadquery Docker organization - the same organization that hosts cadquery/cadquery and cadquery/ocpvtk. But I'll check with the cadquery-server team and see if they can help.

adam-urbanczyk commented 2 years ago

The org has the same name, not sure if it is owned by the same people though. @jmwright do you know who owns it?

jmwright commented 2 years ago

@adam-urbanczyk I just logged in to my Docker Hub account and I'm not a member of that org. I'm guessing @dcowden and/or @roipoussiere know more.

jdegenstein commented 2 years ago

I have encountered a very similar error as the original post when trying to create static builds of CQ-editor with pyinstaller. All recent static builds suffer from this issue including those at https://github.com/jmwright/CQ-editor and my static builds at https://github.com/jdegenstein/jmwright-CQ-Editor. Furthermore I have tested on both Linux and Windows and both have this issue.

I have been trying to fix this issue for a few days now, and even adding the DLLs to the pyinstaller bin and datas parameters has not helped fix the issue (see my pyinstaller.spec). I have not tried anything related to libgfortran, as none of my error messages indicate this.

Here's an error message on Linux:

CasADi - 2022-10-26 11:13:00 WARNING(".../casadi/core/plugin_interface.hpp:322: Assertion "handle!=nullptr" failed:
PluginInterface::load_plugin: Cannot load shared library 'libcasadi_nlpsol_ipopt.so': 
   (
    Searched directories: 1. casadipath from GlobalOptions
                          2. CASADIPATH env var
                          3. PATH env var (Windows)
                          4. LD_LIBRARY_PATH env var (Linux)
                          5. DYLD_LIBRARY_PATH env var (osx)
    A library may be 'not found' even if the file exists:
          * library is not compatible (different compiler/bitness)
          * the dependencies are not found
   )
  Tried '/home/john/Downloads/CQ-editor/casadi' :
    Error code: /home/john/Downloads/CQ-editor/casadi/libcasadi_nlpsol_ipopt.so: cannot open shared object file: No such file or directory
  Tried '' :
    Error code: libcasadi_nlpsol_ipopt.so: cannot open shared object file: No such file or directory
  Tried '.' :
    Error code: ./libcasadi_nlpsol_ipopt.so: cannot open sh... truncated") [.../casadi/core/plugin_interface.hpp:171]

Here's an error message on Windows:

CQ-editor-Windows-mmamba47>call .\CQ-editor\CQ-editor.exe
CasADi - 2022-10-24 16:33:12 WARNING("D:\bld\casadi_1666127168945\work\casadi\core\plugin_interface.hpp:322: Assertion "handle!=nullptr" failed:
PluginInterface::load_plugin: Cannot load shared library 'casadi_nlpsol_ipopt.dll':
   (
    Searched directories: 1. casadipath from GlobalOptions
                          2. CASADIPATH env var
                          3. PATH env var (Windows)
                          4. LD_LIBRARY_PATH env var (Linux)
                          5. DYLD_LIBRARY_PATH env var (osx)
    A library may be 'not found' even if the file exists:
          * library is not compatible (different compiler/bitness)
          * the dependencies are not found
   )
  Tried 'CQ-editor-Windows-mmamba47\CQ-editor\casadi' :
    Error code (WIN32): 126
  Tried '' :
    Error code (WIN32): 126
  Tried '.' :
    Error code (WIN32): 126") [D:\bld\casadi_1666127168945\work\casadi\core\plugin_interface.hpp:171]

I also found this related issue https://github.com/casadi/casadi/issues/2526

adam-urbanczyk commented 2 years ago

@jdegenstein if the file is bundled, try using ntldd -R on windows or libtree on linux to debug those kind of issues.

jdegenstein commented 2 years ago

Alright using procmon on my static build "mmambaTAR-17" I was able to work through all missing DLLs and get assembly.solve() to work. Here is a list of what I had to manually copy:

manually added (from a conda install):
casadi_nlpsol_ipopt.dll
ipopt-3.dll
libblas.dll
liblapack.dll
dmumps.dll
flang.dll
flangrti.dll
libomp.dll
mkl*.dll (a few dozen DLLs)

I will work on streamlining this process for the static builds, now that I know what was missing.

EDIT: The exact MKL DLLS that are needed are the following (based on limited testing):

mkl_core.2.dll
mkl_def.2.dll
mkl_intel_thread.2.dll
mkl_msg.dll
jdegenstein commented 2 years ago

After removing pip install pipwin and pipwin install numpy from the windows build, we are down to just these missing libraries (all from the casadi==3.5.5 package):

libcasadi_nlpsol_ipopt.dll
libgfortran-3.dll
libquadmath-0.dll

The pyinstaller devs say that their binary analysis must be struggling with dynamic loading. cadquery/occ_impl/solver.py has a line for import casadi as ca but then the later options passed to ca.Opti().solver() dictate that it will need mumps and ipopt. I wonder if there is a more explict way of "importing" casadi that will indicate to pyinstaller it needs mumps and ipopt? The casadi package is ~200 MB so if we can avoid adding all binaries it might be a good idea. Any suggestions for getting past this are welcome.

see discussion here: https://github.com/pyinstaller/pyinstaller/discussions/7199

adam-urbanczyk commented 2 years ago

Not that I know of. Casadi uses plugins, so it is loading things ( libcasadi_nlpsol_ipopt.dll ) dynamically at runtime.

jdegenstein commented 2 years ago

I now have a fully working static build (for Linux/Windows/MacOS) that primarily relies on PIP installs rather than conda. This was necessary to fix the issues related to the new solver's use of casadi/ipopt. Another part of the fix at the above link was that the pyinstaller devs advised me to create a hook-casadi.py file and then add to my pyinstaller_pip.spec.

hookspath=['pyinstaller/extrahooks/'],
# hook-casadi.py
from PyInstaller.utils.hooks import collect_dynamic_libs

binaries = collect_dynamic_libs('casadi')

# Something about legacy import codepaths in casadi.casadi causes PyInstaller's analysis to pick up
# casadi._casadi as a top-level _casadi module, which is wrong.
hiddenimports = ['casadi._casadi']
excludedimports = ['_casadi']

This is resolved for me now, but changes need to be made to jmwright/CQ-editor to incorporate some or all of these changes to fix the Assembly.solve issues. I am planning to use PIP as much as possible going forward (only using conda to set up base environment).

adam-urbanczyk commented 2 years ago

Great! Could you explain though how pip specifically resolved the casadi bundling issue? AFAICT it was just a matter of bundling the right shared libs which are not dependent on the specific package manager used. I checked myself on windows and the pip based wheel of casadi also loads the plugins at run time (thus not at load time).

jdegenstein commented 2 years ago

Here is a comment from one of the pyinstaller devs concerning pip/conda at the above discussion:

Yes, stick to pip. Anaconda rearranges the contents of packages into something resembling a UNIX style environment which, as I understand it, is only possible on Windows by weak linking Python extension modules to the DLLs that they depend on. PyInstaller cannot reverse engineer that.

adam-urbanczyk commented 2 years ago

I've seen the comment and I don't understand what it really means. TBH I suspect it is not true (i.e. there is nothing special about how the casadi conda package is linked as compared to the wheel). In the end you had to manually include the relevant casadi libs even with pip. Did you try with conda and manual inclusion of the libraries did not work?