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.
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:
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
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.
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
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)
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.
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.
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