Python cycle for trees with different branch structures

Hi,

I have two lists in grasshopper. The first contains 2D sectional properties of a finite number of shapes and its structured like {A}(i) and i=0 to 7 (area, inertia, centroids, etc.). Each branch refers to a different shape. Let’s say:

The second list has information about how much each shape is rotated and a structure like {A;B;C}(i) with a single item per branch (i=1). The cross reference to the shape is given with path C. So:

I already programmed coordinates transformation routine in python so, that’s done. As for accessing the trees, that’s where I’m having difficulties. So, how could I develop an if / for cycle to:

  1. Go through each branch of the second list
  2. Check what value is stored in {:;:C}
  3. Retrieve all data in the first list where {A} = {:;:C}
  4. Perform my calculations
  5. Go for the next branch
  6. Store whatever variables I computed inside a new list/tree. This list should have a structure matching the second list but with N>1 (N = number of new sectional properties, reflective of the rotated shape):

Have you tried the treehelpers module, to unpack the Datatree however is needed?

1 Like

this looks pretty straightforward also with standard components:

VANILLA_COMPONENTS.gh (15.7 KB)

by the way, in Python you can access data tree info of data tree x with:

for i in range(x.BranchCount):
        path = x.Path(i)
        branch = x.Branch(i)
1 Like

Currently messing with it! Thanks!

Well, I went with python scripting instead of GH components for 2 reasons: 1) I want to learn it and 2) I thought it would be easier to handle large amounts of computations. Anyway your sugegstions to access tree data were helpful! Thanks!

I think I progressed quite a bit but now I’m stuck with generating the output trees with my results in the format I need, i.e., {A,B,C} branches like the original second list with rotation information. I will share what I have in case someone can suggest a solution for the last part of the code.

Particularly, I think I did not fully get this “format”:

x = th.list_to_tree(x, source=[0,0])

Is source=[0,0] suppose to define the branches? If not, that is my problem because I basically stated source=[A,B,C], in an attempt to define the branch {A,B,C} as stated earlier.

My code:

import ghpythonlib.treehelpers as th
import math
pi = 3.1415926535897932384626433832795028841

xc_global_Tree = []
yc_global_Tree = []
zc_global_Tree = []
Iu_Tree = []
Iv_Tree = []
Iuv_Tree= []
Az_Tree = []
Ay_Tree = []
Azz_Tree = []
Ayy_Tree = []
Iy_Tree = []
Iz_Tree = []

for i in range(Points.BranchCount):
    path_points = Points.Path(i) # tree structure {A;B;C}
    path_points_A = (path_points[0]) # A: path for number of surfaces
    path_points_B = (path_points[1]) # B: path for number of cross sections / frames
    path_points_C = (path_points[2]) # C: path for intersecting points that define stiffeners of a given shape
    for k in range(SProp.BranchCount):
        path_sprop = SProp.Path(k) # tree structure {A}
        branch_sprop = SProp.Branch(k)
        path_sprop_A = (path_sprop[0]) # A: sectional properties of stiffeners of a given shape
        
        if path_sprop_A == path_points_C:
            #Retrieve properties at centroid in (x,y) frame - x horizontal, y vertical from SProp Tree 
            xc = float(branch_sprop[0])
            yc = float(branch_sprop[1])
            A = float(branch_sprop[2])
            Iox = float(branch_sprop[3])
            Ioy = float(branch_sprop[4])
            Ioxy = float(branch_sprop[5])
            Sx = float(branch_sprop[6])
            Sy = float(branch_sprop[7])
            #Retrieve rotation angle from deg Tree and rotate local xc, yc
            angle = deg.Branch(i)[0]
            xc_rot = xc*math.cos(pi*angle/180)-yc*math.sin(pi*angle/180)
            yc_rot = yc*math.cos(pi*angle/180)+xc*math.sin(pi*angle/180)
            #Retrieve global coordinate points from Points Tree
            point = Points.Branch(i)[0]
            #Retrieve global coordinate points from Points Tree and calculate centroid in the global coordinate system (y,z)
            components = point.strip('{}').split(',')
            xc_global, yc_global, zc_global = map(float, components)
            zc_global = zc_global + yc_rot #Minus(-) or Plus (x)???????
            yc_global = yc_global - xc_rot #Minus(-) or Plus (x)???????
            #Transformation of Moments of Inertia due to axes rotation
            Iu = (Iox+Ioy)/2+(Iox-Ioy)/2*math.cos(2*pi*angle/180)-Ioxy*math.sin(2*pi*angle/180)
            Iv = (Iox+Ioy)/2-(Iox-Ioy)/2*math.cos(2*pi*angle/180)+Ioxy*math.sin(2*pi*angle/180)
            Iuv = (Iox-Ioy)/2*math.sin(2*pi*angle/180)+Ioxy*math.cos(2*pi*angle/180)
            #Steiner theorem to move properties to global reference system (YoZ plane)
            #Moments
            Az = A*zc_global
            Ay = A*yc_global
            #Steiner term
            Azz = Az*zc_global
            Ayy = Ay*yc_global
            #Area moment of inertia with respect to global axis (Steiner theorem)
            Iy = Iv + Azz
            Iz = Iu + Ayy

    #Construct output Trees in a structure type {A;B;C}
    xc_global_Tree = th.list_to_tree(xc_global, source=[path_points_A,path_points_B,path_points_C])
    yc_global_Tree = th.list_to_tree(yc_global, source=[path_points_A,path_points_B,path_points_C])
    zc_global_Tree = th.list_to_tree(zc_global, source=[path_points_A,path_points_B,path_points_C])
    Iu_Tree = th.list_to_tree(Iu, source=[path_points_A,path_points_B,path_points_C])
    Iv_Tree = th.list_to_tree(Iv, source=[path_points_A,path_points_B,path_points_C])
    Iuv_Tree = th.list_to_tree(Iuv, source=[path_points_A,path_points_B,path_points_C])
    Az_Tree = th.list_to_tree(Az, source=[path_points_A,path_points_B,path_points_C])
    Ay_Tree = th.list_to_tree(Ay, source=[path_points_A,path_points_B,path_points_C]) 
    Azz_Tree = th.list_to_tree(Azz, source=[path_points_A,path_points_B,path_points_C])
    Ayy_Tree = th.list_to_tree(Ayy, source=[path_points_A,path_points_B,path_points_C])
    Iy_Tree = th.list_to_tree(Iy, source=[path_points_A,path_points_B,path_points_C])  
    Iz_Tree = th.list_to_tree(Iz, source=[path_points_A,path_points_B,path_points_C])


I never set source, I just call list_to_tree on my lists without problems.

To create a more complicated Datatree structure, I’d create a Grasshopper.DataTree[object] and use that API directly.

1 Like

not at the computer, but I have some free time and have to wait for half an hour anyways so here is my thought, but consider I can’t test it so it will NEVER work if you just copy paste that :slight_smile: also, this comes solely from good sense and a lot of imagination: it could also be wrong in principle :innocent:

initial data are tree_1 and tree_2
each branch of tree_1 contains multiple items and simple path like {0}
each branch of tree_2 contains the rotation values and complex path like {0;1;2}

I imagine it might work like:

  1. create an empty data tree that you will populate with branches and items
  2. loop over tree_2, for each branch get its full path, isolate path last number
  3. start a second loop that iterates through tree_1 looking for the correct path that matches tree_2 current branch last path_number
  4. once you find the right branch of tree_1, iterate through all items of that branch to calculate new values, and populate a single new branch of the output tree with those calculated values
  5. once the last new value is calculated, break the second loop (the one through tree_1) and restart the whole thing on next branch of tree_2
from Grasshopper import DataTree

# create an empty data tree to hold the results of calculations
result_tree = DataTree[object]()

# go through each branch of tree_2
for i in range(tree_2.BranchCount):

    # will use the following path to create output tree with same structure
    tree_2_full_path = tree_2.Path(i)
    
    #go through each branch of tree_1
    for j in range(tree_1.BranchCount):
        
        # and look for same path until BINGO!
        if list(tree_1.Path(j))[0] == list(tree_2_full_path)[-1]:

            # populate a branch of result_tree with all the calculated values
            result_tree.Add('calculated_value_1', tree_2_full_path)
            result_tree.Add('calculated_value_2', tree_2_full_path)
            result_tree.Add('calculated_value_3', tree_2_full_path)
            # each value you calculate populates the very same branch
            # and that branch path comes from the tree_2.Path(i) which is the
            # current branch being explored in tree_2

            # once calculation is done, skip remaining branches of tree_1 
            # and go directly to next branch of tree_2 to do the same again
            break

I have no idea if there’s a very fast way to separate path address by “;” with a dedicated function the same way the component Deconstruct Path works… using string slicing looks overcomplicated :frowning:

[edit_2] of course there was, you don’t need at all to go through string slicing (replaced in the above code)

path = data_tree.Path(i)
indices = list(path)  #[0, 1, 2]

:+1:

1 Like

Thank you @inno and @James_Parrott, I’m not on my pc as well at the moment.

I will give feedback as soon as I test this!

Thank you!