GH_Document.NewSolution crashing Rhino

Hi,

I’m working on an optimization component in Grasshopper.
During the optimization process, I need to repeatedly update the definition to calculate the newest fitness value. However, I’m having trouble with the updating.

Specifically, when I run the below test code in a C# component that is not connect to anything else, Rhino crashes:

for(int i = 0;i < 3; i++){
currentDoc.NewSolution(false);
}

The same is true for this code:

currentDoc.NewSolution(false);
currentDoc.NewSolution(false);

I suspect that the C# component expires itself, and that this throws it into an infinite loop.
(Strangely this only seems to happen if the solution is expired more than once.)
Any ideas one why this happens, and what I need to do differently?

Thanks,
Thomas

I think that @DavidRutten will be fitter than me at answering with first-hand knowledge, but I’ll tell you my understanding.

Calling currentDoc.NewSolution() from a component will expire the component itself, and eventually make this component re-compute itself. This will call currentDoc.NewSolution() again. And that will make it call it again. […] This will be a non-stopping recursive call and the .Net stack will finally overflow.

Giulio

Giulio Piacentino
for Robert McNeel & Associates
giulio@mcneel.com

… to see if this is the case, just add a field like this:

bool inRecursiveCall;

before calling currentDoc.NewSolution(); , check if you are already solving the solution…

if (inRecursiveCall) return;
//[...]
inRecursiveCall = true;
currentDoc.NewSolution();
inRecursiveCall = false;

NewSolution() will not expire the component it was called from, for that you need to call ExpireSolution(true) on the object. However if you keep starting new solution from within other solutions, you’ve basically made a never ending recursive call stack which will crash Rhino hard.

You can not start new solutions while you’re already running a solution. Nor can you change or expire components during solutions.

If you need to make modifications to components or parameters, then you need to do so after a solution finishes or before a solution starts. There are events that you can subscribe to (GH_Document.SolutionStart and GH_Document.SolutionEnd).

A better approach is to schedule a solution. You can do this any time you want, even during a solution. When a schedule exists, it will trigger a new solution some milliseconds after the previous one finishes. However before this happens you can get a callback via a delegate which will allow you to make all the changes you want.

A better approach still in cases like this is to write some code that executes completely separately from the GH_Document. That is what Galapagos does. It shows a new window and the code running in the window is in charge of changing sliders and triggering solutions. It means the code looks very much like what you have already, but because it isn’t executed as part of the GH_Document, it’s safe:

while(true)
{
  if (we'refinished)
    break;
  // Assign or modify data, perhaps even add or remove components and add/remove wires.
  _document.NewSolution(false);
}

Thanks Guilio and David!

So how do I separate my code from the Grasshopper document?
(Right now I’m prototyping it in a C# component, but I haven’t found a way to make this work.)

Should it be a separate C# program that is started from inside Grasshopper?
(I guess that I need to pass it the current Grasshopper document, right?)
Would it be a part of the Grasshopper assembly that I’m making for my components?

Thanks,
Thomas

It doesn’t need to be a separate executable. Having the code in a separate window helps, but even that isn’t necessary. The only important thing here is that the code isn’t called again during every solution. You can either do this using Giulio’s private bool suggestion, or find some other way to start the ball running safely.

I have to go shopping now, but I can give you an example using a window later.

@David
An example would be great, thanks!

window code.gh (8.4 KB)

The attached file has a C# component which shows a window with one button. The button click event handler starts a loop with 100 iterations. Every iteration adjusts all sliders plugged into the component and then runs a new solution.

1 Like

Awesome, thanks!

Hi David,

Many thanks for your example - it got my optimization component running (more or less).
However, I have a couple of other questions now about running code in the window.

  1. How can I throw an exception, and output it somewhere? The exceptions I was throwing in the window were making Rhino crash. I’ve tried replacing them with GH_ActiveObject.AddRuntimeMessage, but that only works some of the time. Most often the messages don’t show up. (My guess is that this has something to do with (not) recalculating the component.)

  2. What’s the best way to print out some data from the code that’s running in the form. (How) can I get access to the IGH_DataAccess DA of the component? Alternatively, I’m looking into using GH_Param.AddVolatileData Method (GH_Path, Int32, Object), but I’m not sure what to use for the path and the index variables.

Thanks,
Thomas

That won’t work any of the time. AddRuntimeMessage is not a static method and needs to be called using an actual instance of a GH_ActiveObject. You could store a reference to the script component in your window and call the method on that one, but the messaging UI is only updated after a component finishes solving itself. And the messages are erased whenever the component expires.

I recommend putting the error messages directly on the form. Perhaps add a Windows.Forms.Label or Windows.Forms.TextBox control in addition to the button.

Exceptions always crash an application, unless an exception handler catches them first. The entire grasshopper solutions runs inside an exception handler block, as does each component. So any exceptions thrown there are caught by me and handled before they crash Rhino. When you’re running code from a custom window you’re in charge of handling your own exceptions.

You can always print messages to the Rhino command line using Rhino.RhinoApp.WriteLine(), but those messages disappear from view quickly so that may not be what you’re looking for. Putting a Label or TextBox on your form gives you an area where you can write text to as well. The big benefit of using a TextBox is that the strings are actually copy/paste-able, in case people have to send you information.

Ultimately, you can even draw custom things on the Grasshopper canvas. You can create a class which derives from Grasshopper.GUI.Canvas.TagArtists.GH_TagArtist and then implement the Paint() method. This gives you access to several channels in the canvas drawing logic, you’ll probably want to draw in Overlay if you want to show messages on top of everything else.

Alright, thanks :slight_smile:
Just one more question:

Do I understand you correctly in that there is no way of setting data to a component output from outside the component itself?

I guess that makes sense, but it would be useful, for example to have a Boolean value that indicates if the optimization is currently running or not.

It’s probably possible, but I can’t even begin to imagine the nightmare complexity of that code. First of all the data needs to be assigned during solutions. But after the component erases all its outputs. Then you need to make sure that the component doesn’t overwrite the output data. If the component you’re trying to manipulate is one of your own, it’s probably doable, but if it’s someone elses, I really wouldn’t recommend going down that path.

It’s probably somewhat easier to poke floating parameters instead of components, especially if they don’t have any inputs.

@DavidRutten … Would it be possible to reupload the file for the window code.gh? I know it’s been 5 years since this thread was last active but the file seems to no longer be available and I think it would be really helpful for the problem I’m currently having… (I’m trying to figure out a way to separate my optimization window and the Grasshopper component code for an optimization). But if you no longer have it, it’s fine.

2 Likes

Thanks @dsonntag, I was indeed going to suggest that the solution is probably in there.

1 Like

Ooooh, Thank you so much!!! That will be really helpful! :grinning:

Haha don’t thank me thank Thomas :wink: