Grasshopper Export from External Python script

Hello,

Is it possible to run Grasshopper from an external python script in order to modify the geometry parameters and export a geometry? I think that GH Python Remote can achieve what I am trying to do but not sure how to implement it.

My simplified GH model is attached:

Which already includes a script that exports to the required format.

The problem is that I am not sure how to use GH remote to modify the parameters and run the model… any tips? I have taken a look at the example files provided, but not sure how to apply them to my case…

The reason of using an external puthon script is that I am trying to implement an optimizaton framework, which analyses the geometries (calling to different softwares such OpenFOAM), and selects the new geometries to analyse.

Thanks!

export_simple.gh (14.3 KB)

The export format is STL, but the script I made works already.

Obviously, the geometries I want to analyse are more complicated than a box, but it is just for testing purposes.

Any help or suggestions is appreciated.

Hi Javier -

If I understand your question correctly, I believe that this is in the works. At least for Rhino ComputeTM but perhaps that would also work for Rhino.Inside Python.
@stevebaer can probably tell you more.
-wim

Thanks for your answer Wim. I didn’t know the existence of Rhino compute, I will have to take a look at it.

What already had in mind was the GH Python remote:

Apparently (if I understand the description correctly), already has the capacity of running Rhino/Grasshopper from an external python script. From the help:

“You can also use gh-python-remote to programmatically control a Rhinoceros instance, and connect to it via Python. Have a look at examples/python_to_GH.py for a full working example.”

Unfortunately, this example python script opens a Rhino file (rather than a Grasshopper) and performs some operations on it… not sure how whether I can use this plugin for a Grasshopper definition or how to do it. Eventually, the aim is that the external python script can modify the geometric parameters defined in the Grasshopper definition and export an STL file to the specified path.

Hi Javier,

What can be done in GH Python Remote, from CPython to Rhino, is only to interact with the Rhino Python engine directly. There is no explicit mechanism to control the GH canvas and solver in it. But you can access all the NodeInCode components, which include all your GH components, and call them just like a regular Python function.

However, since Rhino 6, there is no way to call GH User Objects from Python anymore, so you are restricted to using compiled .gha components. So you have to reproduce all the logic in your GH file as Python code calling GH Components. You will not be able to use .ghpy files, or anything like Ladybug/Honeybee which are user objects. edit: workaround available in GH Python Remote v1.3.1, see below.

You are basically limited to the same restrictions as when running RhinoPython (directly in Rhino, not in GH), and calling NodeInCode components. This means you might be able to use the same kind of workarounds to maybe control a GH solver in Python:

But maybe using these directly, without GH Python Remote will be easier to achieve what you are looking for?

Lastly, I have to admit the code for that part of GH Python Remote is a bit dated and I’ve not updated it frequently as it was not used much. To make it work you’ll need a few manual modifications (on top of having installed gh-python-remote v1.3.0, and using Rhino 6):

(Right-click the “Raw” button on the top right of file view and select “Save as…”)
edit: this was fixed in GH Python Remote v1.3.1

I hope this helps!

1 Like

Hi Pierre,

Thanks a lot for your answer! I think I understand your points, but not totally sure… I need some time to process all the information.

If understand correctly, one workaround will be to work re-rewrite the whole GH model and all its logic in a Python format, using NodeInCode components, and call it from the script?

So there is no chance to use an existing Grasshopper model? (for example updating the sliders like in the second link you posted, and then checking the output of a further-down component)

Anyway, it seems that this is way more complicated than I thought. I will have to experiment a bit longer with Rhino, Python and Grasshopper, as I feel my knowledge is not enough for this at the moment.

Thanks again.

Hi @pierrec,
Can you point out how that was possible in Rhino 5? Thank you in advance.

Just realized my old hacks actually still work in Rhino 6!

I used to hack into ghpythonlib.components in Rhino 5, basically just telling it to not skip over user objects when it’s building the list of accessible GH functions. For some reason I thought the new Node in Code infrastructure meant that was not possible anymore and never really tried it. Your comment made me try again, and with a bit more hacking than in Rhino 5 I just got it working in Rhino 6.

If you replace %APPDATA%\McNeel\Rhinoceros\6.0\Plug-ins\IronPython (814d908a-e25c-493d-97e9-ee3861957f49)\settings\lib\ghpythonlib\components.py with components.py (13.0 KB) (and restart Rhino), you’ll be able to access your User Objects from RhinoPython.
Edit: install GH Python Remote v1.3.1 instead. If you’d rather just download one file, copy ghuserobjects.py in Rhino IronPython lib folder and import ghuserobjects

For example, to call a UO named TestCluster:

import ghpythonremote.ghuserobjects as ghuo
result = ghuo.TestCluster(*args, **kwargs)

A bit of warning though: I barely tested anything here and copy-pasted code in the most unsafe and ugliest possible way, so use at your own risk and make sure you keep a copy of the original components.py somewhere. You might run into issues if you have User Objects that use other User Objects or Python components, and if you modify User Objects after having already loaded them. Please report them here if you do.

@piac do you think it would be interesting to have that by default in ghpythonlib?

1 Like

This is wonderful Pierre!!!
Thank you for sharing it with us!

@javier.vinuales.nava Given my previous message, and if you are ready to do the modifications I listed with GH Python Remote v1.3.1, I now have an easier route to propose to you.

Dump you whole GH script in a cluster, and carefully label all the inputs and outputs of it. Use names that would be usable for the arguments of a python function. Then save and go back to the original canvas, and export that cluster as a user object (File > Create User Object). Give it a name that can be used for a python module name.

In GH Python Remote, you will be able to access it as:

from ghpythonremote.connectors import PythonToGrasshopperRemote

with PythonToGrasshopperRemote(
    <path to 3dm file>, <path to ghcompservice.py>, rhino_ver=6, timeout=60
) as py2gh:
    rghuo = py2gh.gh_remote_userobjects
    print(rghuo.<Cluster Name>(<args>))

Argument ordering is the same as what you see on the cluster, and you should also be able to use keyword arguments from the cluster arguments’ names.

Yes, or use the Cluster / User Object trick I just described so you only have one function to call from Python.

I have never done it, but I think you will be able do that the same way from GH Python Remote as from RhinoPython, judging from that script Use pythonEditor to run grasshopper. Just transform the imports import Rhino etc. to Rhino = rgh.modules.Rhino, and the rest looks like it should work.

I did some cleanup and moved the code to import GH User Objects in RhinoPython in a separate module, so you do not have to mess with Rhino builtin files for ghpythonlib. I also released a new version of GH Python Remote that fixes the issues I mentionned above, and includes the User Objects importer.

@djordje @javier.vinuales.nava I edited my previous messages to reflect that, have a look at them for solutions that involve no manual hacks anymore :). Basically boils down to installing GH Python Remote v1.3.1!

3 Likes

Great stuff Pierre! Thanks a lot for your efforts finding a workaround and updating the GH Python Remote. I will be testing this during the next few days.

Just to make sure, in order to install 1.3.1 I shall only run the following commands, right?

pip install gh-python-remote --upgrade
python -m ghpythonremote._configure_ironpython_installation

Just asking because the output mentions 1.3.0 rather than 1.3.1:

Successfully installed gh-python-remote-1.3.0 plumbum-1.6.9 rpyc-4.1.4

Yes, I just missed pushing a button at some point and the release did not make it to pypi. 1.3.1 is out for real now.

Hi Pierre,

Having some problems with the method, I hope you can help me.

Firstly, if I am going to call a grasshopper user object. that contains all the information about my model… what 3dm file I need to reference? Can it be an empty one?

Secondly, I am getting some some error when running a Pyhton script and calling PythonToGrasshopperRemote… A rhino instance opens but I get some errors:


Thanks.

You can set that argument to None to open an empty Rhino.

I don’t understand how you get the second message. This is an internal import to ghpythonlib, not something directly controled by gh python remote. Do you also get that message when you manually run import ghpythonlib in RhinoPython?

There is a good chance that the first error is actually showing what is happening, but it’s ahrd to tell exactly. Again, I assume you do not get this error when manually loading GH? Is there a way to copy-paste the error code somehow, so I could read what it is saying? Or maybe just a screen shot showing more of the text in the first column? Any reference to a specific GH plugin or user object somewhere?

Hi Pierre,

Thanks for your answer. Apparently, there was some kind of problem with my Rhino installation… after re-installing it seems that the errors are gone and the user object methodology seems to work for initial test.

1 Like

Hi again Pierre,

My initial test worked, which was composed of one text output as such:

image

The statement: print(rghuo.hello()) where hello is the name of the user object will return hello! as expected, but anything more complicated than this won’t return anything. For example, using a component to join some strings like the following:

image

and using again print(rghuo.hello_world()) where hello_world is the name of the user object, won’t work and it gives the following warning:

No handlers could be found for logger "ghpythonremote.connectors"

Any idea why is not working?

Thanks for your help.

I’ve tried lots of things and can’t reproduce the No handlers could be found for logger "ghpythonremote.connectors" error. Do you have a way to reproducible way to get it?

Your problem is probably with the underscore in the UO name. Does it work if you recreate the UO without it?

I’m looking at ways to improve both available names and logging, I’ll include that in the next version.

1 Like

Avoiding underscores worked! thanks again! My script test handled inputs to modify the geometry and exporting the required data to export an STL file.

By the way, do you know a way to select the plugins and user objects that are loaded with Grasshopper? they take some time to load (and during the optimization will need export 100-1000s of geometries… so the total loading time could end up being significant) and one of the plugins I use (Millipede) has an annoying warning when loaded that needs to be manually closed (stopping the script to run autonomously!).