Syntax error in Python_Steiner tree

Hello everyone,
Greetings to all.

1.I am trying to write the script written on .NET in ghpython. And I am unable to get the same output as in .NET

It takes list of points as input and then draws a tree out of it and then finds the nearest point using it. A minimal path network. (20.1 KB)

Unfortunately, I am not a C# wiz and I have started to get my hands on learning Python.
Any help would be much appreciated.

Thank you for your time in advance

Hi so are you getting a Syntax Error in python? What is the python error message? What is the line giving the error?


I tried solving the syntax but the output is null.

Which method I should use to read list access points in python and convert them in point3d.


You seem to be using an upper case A for your variable and a lower case a for your output…

side note: for posts that contain code formatting, you can enclose any code snippets with triple single quotes, put 3 single quotes above your first line of code, and 3 after the last line of code.

#triple quotes are above this line
for i in range(0,10)
  print i
#triple quotes are below this line.
1 Like

Hi @Arya,

I’ve managed to transcode it to python.
Feel free to check it out below. (14.0 KB)


Hello Pb0y,

Thank you for trying it.This works great, much more cleaner and direct than my syntax.Perfect!
I was missing the use of data tree and mid point.

I wanted to know if there’s a possibility of keeping 120 degrees as a part of my input rule in python, instead of using Kangaroo and constant tension(to achieve 120) after getting the output.
So the python script will detect the nearest relative neighborhood points and make an angle of 120 degrees in between them.

Achieving this (output after relaxing lines in kangaroo)as a python output

instead of this(output of python) currently


Hello @Arya

It sure should be possible somehow!
Is the Steiner tree always defined by 120° angles or is this just your preference?
Do you already have a strategy on how to mathematically or geometrically achieve this in mind?

I don’t think it’s possible to generate the network topology (or geometry I suppose) directly with the 120 degrees angles using this algorithm. As this is an iterative point pairing/inserting process (and thus angles might change at each step). Hence the need for another iterative process downstream to relax the network (by either minimising the edge lengths or using the constant tension goals as Daniel explained).

For reference, in our video/algorithm (that you and @Petras_Vestartas referred to in the other thread) we implemented the Kangaroo relaxation process as a Python function (by implementing the KangarooSolver.dll directly) within the same GHPython component as the function that generated the network topology.

I suppose my question would be why you feel you need to skip the Kangaroo/relaxation process?

1 Like

@AndersDeleuran, could you please clear some things up about your kangaroo Python implementation (that I found on GitHub)?

# Reset state
if Reset or "counter" in globals():
    # Make physical system and goals list (using .Net type list)
    ps = ks.PhysicalSystem()
    goal_list = List[ks.IGoal]()
    # Add goals to system and goals list
    for goal in Goals:
        ps.AssignPIndex(goal, 0.01)
    # Make static counter variable
    counter = 0

# Run state
    if Step:
        ps.Step(goal_list, True, 1)
        counter += 1
  1. What does ps.AssignPIndex(goal, 0.01) exactly do?

  2. Would it be possible to construct complex goals in nested lists and assign those, like you would for instance entwined goals in Grasshopper before plugging them to the solver.

  3. Could ConstantTension simply be called with ks.Goals.ConstantTension() and how do I know what to pass in?

  4. What does the True and 1 mean whilst stepping?


That is very old code, from back when we were first implementing Joey/K2 in GHPython. I’ll see if I can dig up some more up-to-date examples for you.

It assigns a particle index for the particles (i.e. points to operate on) in the goal. That is, instead of managing the topology of the particles/goals yourself, this method handles that for you. This is slower, but simpler. Though, one of the benefits of scripting K2 is exactly managing the topology of the system.

Yes it would, you can really do whatever fits your needs the best. This is a key advantage of scripting K2 IMO, in that one can actually simplify things quite a lot (as compared to the somewhat hoop-jumping of structuring/re-structuring datatrees). As I recall we also added a property on the goals that allowed one to tag them with a string (for explicitly naming/structuring the goals data), but I think that was only for the goal output. I forget, anywho. Yes you can.

Yes, exactly. You would go something like:

import clr
import KangarooSolver as ks

# Make goals list
goals = []
for l in lines:
    if mode == "spring":
        g = ks.Goals.Spring(l.From,l.To,0.00,1.00)
    elif mode == "constantTension":
        g = ks.Goals.ConstantTension(l.From,l.To,-1.0) 

It can admittedly be a bit difficult figuring what the input parameters mean, but maybe just have a look at the corresponding component, which is generally well documented:


Though that can also be confusing, since for instance the Spring goal is named Length in its component implementation.

I forget exactly (ping @DanielPiker), but I believe it’s ps.Step(GoalsList, RunParallel, KineticEnergy). One could also use the simpler ps.SimpleStep(GoalsList) when implementing a solver. There’s also the momentum step method, and I think one more :thinking:

1 Like

Thanks @AndersDeleuran, great explanation.
All I’d add is:
re. input parameters for the Goal classes, you can also look at the code for each goal to see exactly how these values are being used:
For the Step method, the only important input is the list of goals.
RunParallel sets whether to use parallel processing - you can always leave it as True. The option is in there because for very small simulations the overhead of parallel methods can sometimes outweigh the gain, but we’re talking a millisecond or 2, so really not worth thinking about.
KineticEnergy is a threshold for stopping within that step if an energy threshold is reached, but since you should have some other method of stopping steps, there’s rarely any need to do this, so setting it high enough that it is never triggered is easiest, just leave it at 1.0

Ah yeah, forgot you made those public. How time flies :older_man:

Hello Pboy,

Since 120 is the equilibrium angle of a Steiner tree, imitating the same from frei Otto’s experiment on bubbles. I believed that 120 degrees could be a part of my input rules and then I would evaluate the different iterations I get for other fitness criterias.
So the Python syntax would have degree and minimal path as constant rules.

Hello Anders,

Yes even I feel the same.I can’t really figure out the strategy mathematically to achieve constant tension in the ghPython syntax itself.
The angles are changing according to each point getting paired with its nearest neighborhood.

I am just inquisitive on the fact of using ghPython to do what kangaroo constant tension is doing, the output after constant tension in gh is not exactly 120 degrees if you would see here which is presenting a problem for me in optimizing the outputs furthermore.

The count is 5 here, as I increase the count the angle of 120 vanishes slowly as constant tension balances out the pull points as much as it can.

Hi Arya,

If you aren’t getting 120° angles, something is set up wrong. Kangaroo will converge to 120.0000° in seconds for this sort of thing. Are you sure you have the MaxIterations set high enough? I suspect you are just letting it stop before convergence.

As for doing it without Kangaroo - you could certainly try other approaches, but I’d be surprised if you find any way to do this without implementing some form of iterative/numerical method.
From your question I guess you are imagining some sort of constructive or analytical method (like for example, how you might solve a simple kinematics problem with intersecting circles).
As far as I’m aware no such method is known for finding general Steiner trees in 3d.

Whenever a simple constructive solution to a problem exists, it does usually make much more sense to use that, but there are many whole classes of problems for which we simply do not have any analytical methods of solving them, and the only way we can get an answer is by numerical methods. Surprisingly, sometimes these can even be problems which are simple to state and only involve satisfying simple geometric conditions.

Coming back to Steiner trees - the methods posted above may still break down if some segments approach zero length after relaxation. This can be solved by allowing the graph to reconfigure itself between iteration steps - I did once write something for these topological changes - I’ll see if I can find it and dust it off.

Thank you so much for describing it so well Daniel.
I understand now.
As for now I am using Kangaroo constant tension until I change my limits further.
For this particular algorithm, I am able to achieve minimal length and a degree of equilibrium, what I intended to do from the very start.
Indeed, I need to change my MaxIterations everytime I increase or decrease the count.
And yes the segments do break off while approaching zero length.
Thanks again

Hi @Arya,

You can simply add these components to remove the points that are inside of the sphere, before passing them on to GHPyhton and other components.

The B input of BrepInc is connect to the sphere component!

Hello again Pb0y!

I did try that.
But the lines are still getting inside the sphere even if there are no points.

The syntax should ignore point 2,even if it is shortest because it is entering the boundary of the sphere and instead connect to point 3 which is outside.
This will make you understand better-

You think this demands another python syntax.:thinking:

The simplest way would probably be to check the polylines, constructed from the output nodes points, for intersections with and inclusions in the sphere. You could then simply cull the desired curves too.

If I remember correctly, my version of the Python script only provided ordered points that you could construct lines from. In order to check, if a connection pierces the sphere somewhere, you’d probably also have to construct a line segment first, and check for intersections with or inclusion in the sphere in code. At the moment, I can’t think of a better way.