Question on ExpireSolution in GhPython

Hi everybody

I have faced a simple problem and as I am new to this field I could not solve it. I want to write a simple code in GHPython that could change the value of a slider until it reaches to a certain point that the area of a circle with the radius of that slider passes 12. I have attached the files and the photos. Any help is very well appreciated.

question.gh (6.6 KB)

That’s because you’re not allowed ‘expire’ upstream components during a solution, that would violate the dataflow-programming principles of Grasshopper.

See the forum posts below for more detailed explanations

Your scenario sounds like something more suited to Galapagos:

Are the steps important? Or just the result. You can reach those results without steps or Galapagos and results will be exact.


ConstrainedAreaCircle.gh (7.7 KB)

Thank you for your comment. Actually I do know how to use Galapagos for this task, but I need to do this through python code. I am working on an app and I need to work with Expire Solutions but I don’t know how to use it inside python. I went through a lot of documents but most of them were in C# or VB and I could not find how to code it in GHPython. It is appreciated if you could help me on how to code it inside GHpython

Thank you Micheal for your help and grasshopper file. As I replied to previous comment, I need to code the Expire Solution in GHpython and I use this question as a sample to learn how to code Expire Solution. I have no idea how to do this and I didn’t find any documentation regarding this issue in GHpython. It would be helpful if you could show me how to code the Expire Solution in GHpython for this task.

Here’s what I came up with, using the scriptcontext.sticky dictionary which retains data between solutions. Note that you can directly access the instance properties instead of using the GetValue and SetValue methods of the GH_NumberSlider type.

from scriptcontext import sticky

thisDoc = ghenv.Component.OnPingDocument()
slider = ghenv.Component.Params.Input[0].Sources[0]

if click_me:
    # reset values to starting position
    sticky['running'] = True
    slider.TickValue = 30 

desired_area = 12
if sticky['running']:
    if area > desired_area or slider.TickValue >= slider.TickCount:
        sticky['running'] = False # break the loop
    else:
        def sln_delegate(doc):
            slider.TickValue +=1 
        thisDoc.ScheduleSolution(1, sln_delegate) # call the delegate function and schedule a solution in 1 millisecond

Alhough if you’re comfortable enough with Python and RhinoCommon scripting, you could get a 100x speedup by embedding the entire loop logic inside the Python script:

from Rhino.Geometry import *

num_steps = 10000
min_radius, max_radius = 0.030, 10.00

for i in range(num_steps):
    radius = min_radius + i * (max_radius - min_radius) / num_steps
    circle = Circle(Plane.WorldXY, radius).ToNurbsCurve()
    area = AreaMassProperties.Compute(circle).Area
    if area > 12:
        break


question.gh (9.3 KB)

thank you :wink:
this code was really helpful and I learned how to work with it

hi again,

I worked around your code and learned few new topics. Here I face a new problem and I could not recognize why I have 3 outputs.

thisDoc = ghenv.Component.OnPingDocument()

slider = ghenv.Component.Params.Input[0].Sources

if click_me:
def sln_delegate(doc):
for i in range(len(slider)):
slider[i].TickValue +=1
thisDoc.ScheduleSolution(1, sln_delegate)

print (x)

would you please have look on this code and help me

3_outputs!.gh (8.5 KB)

Right click on the input parameter and change it from Item to List Access - If you connect multiple sources to a parameter, the default behaviour is to treat each as a separate input and run them in sequence.

Understood. Sorry I am using C#, dunno python. Looks like you got your answer now from qythium.

thanks. great help :pray:

I am still working on different codes and trying to learn more. I have a new question. I think it is a simple question but I could not figure it. I will mention it here and I would also appreciate any documentations which fully describes issues regarding expire solutions.

On my new code I want to iterate through a for loop and call a defined function. The function changes the slider numbers and returns the answers but I don’t know what to write in this case to have all iteration results printed.

import random

thisDoc = ghenv.Component.OnPingDocument()
slider = ghenv.Component.Params.Input[0].Sources

def Allocate(x):
    for i in range(len(slider)):
        slider[i].TickValue = x[i]
    return y

func = Allocate

for i in range(100):
    numbers = []
    for i in slider:
        numbers.append(int(random.uniform(0, 10000)))
    answer = func(numbers)
    print answer

for loop solution problem.gh (10.4 KB)

I also tried this code but it still has errors:

from scriptcontext import sticky
import random

thisDoc = ghenv.Component.OnPingDocument()
slider = ghenv.Component.Params.Input[0].Sources


i = 0
if click_me:
    if i > 10:
        sticky['running'] = False # break the loop
    else:
        def sln_delegate(doc):
            numbers = []
            for i in slider:
               slider[i].TickValue = int(random.uniform(0, 10000)
            i = i + 1
            print y
        thisDoc.ScheduleSolution(1, sln_delegate)

for loop solution problem 2.gh (8.1 KB)

Hi Alex,

I’ve looked through your code and still don’t quite understand what you’re trying to achieve. Here’s another forum post with helpful info from @DavidRutten, and you can probably search and find other such posts scattered through the old forums.

The advice however is usually to avoid such ‘hacks’ unless really necessary. Is there a specific problem you’re trying to tackle, or are you just experimenting with the interface?
I’ve found that the traditional ‘for-loop’ model / mindset can usually be reframed in a more native Grasshopper manner by using Lists and Trees, which perform implicit iteration over their values.

A few more notes:

  • Any for loop in your Python code occurs entirely within the component, before triggering updates on other items on the Grasshopper Canvas. This is why you see the same result being printed 100 times – the slider value changes on screen but the effects don’t propagate till the next solution. In fact, there should be a big ‘Object Expired’ warning box that pops up whenever this happens, unless you’ve suppressed it.
  • You’re using the same variable i multiple times as the counter for your inner and outer loop, that’s never a good thing!
  • Any command that causes other components to expire (eg. changing TickValue of a slider) should be placed inside a GH_ScheduleDelegate callback. As far as I know, you can’t pass additional arguments to this callback - although sticky variables can be used for that purpose.

Here’s just one simple way of doing the above with lists and datatrees

thank you qythium

The point that I am using the for loop is to learn how to use it for optimization algorithms. In optimization algorithms we make populations in different iteration and each iteration sends its variables to outside of the python component and receive the answer and evaluates it. So I need to define the iterations inside the python. I thought I should do it with for loop. Is there any other ways which we could do this?

thank you for the second code but actually I need to define it inside python.

@DavidRutten

Ah, so you’re trying to ‘roll your own’ Galapagos using Python … I think the basic approach would be to externalize the for loop and use sticky dictionary as a counter, i.e. treat the Python component as part of your inner loop and schedule a new solution each time - this way it ensures that everything upstream is re-evaluated between ‘iterations’:

import random
from scriptcontext import sticky
thisDoc = ghenv.Component.OnPingDocument()
sliders = ghenv.Component.Params.Input[0].Sources

if click_me:  # reset
    sticky['counter'] = 0
    sticky['results'] = []

if sticky['counter'] < 100:
    sticky['counter'] += 1
    sticky['results'].append(y)  # result from previous iteration
    values = [random.randint(0, 10000) for _ in sliders]

    def Allocate(doc):
        for slider, value in zip(sliders, values):
            slider.TickValue = value
    thisDoc.ScheduleSolution(1, Allocate)

for res in sticky['results']:
    print res


externalize_loop.gh (11.1 KB)

Great help and thank you for the code :pray: :pray:

1 Like