How to find where a geo node group is being used? Blender 3.4.1

How to find where a geo node group is being used? I have a lot of geo node groups and of course a bunch have name suffixes of .001 or .002 How can I easily find which objects are using the .001 and the .002 so that I can consolidate all objects to use the same groups?
Is there any way to list out a dependency graph of node groups?

1 Like

Maybe in the modifiers panel.

your best bet is to write a python script that inspect each objects modifiers,
There isn’t something in the UI that does that yet…

1 Like

There is nothing built in to do the work. @sozap is right that python can get the dependency graph. Do this to get it written to a file:

  1. Using Blender open the file. In Blender open the python console.
  2. Run the python command “import bpy”
  3. Run the python command “bpy.context.evaluated_depsgraph_get().debug_relations_graphviz(”/SomePath/SomeFile.gv")"
  4. Using a text editor edit the somefile.qv and realize that the DOT syntax is not human readable.
    @Modron is correct in that the Properties panel for modifiers (the wrench) Geo Nodes has at the bottom has a drop down “Internal Dependencies” which can be clicked. So the practical solution is to click on each object one at a time and look at the internal dependencies.

The python is from https://blender.stackexchange.com/questions/220328/visualize-depsgraph I could not get graphviz to install so could not use that to view the graph.

That sounds a bit complicated for what it is…
You need to know which object is using which modifier ?
try this :

import bpy

for ob in bpy.data.objects :
    for mod in ob.modifiers :
        if mod.type != "NODES" : continue
        print(mod.node_group.name,'/' ,ob.name)
    

This will print in the console a list of geo nodes name and their corresponding objects :
image

7 Likes

can you explain how this is working ?

how does it make the difference between normal nodes and Geom nodes ?

thanks
happy bl

1 Like

Perfect. Thank you.

1 Like

Here is the breakdown of the code :

for ob in bpy.data.objects :
For all the objects in the .blend

for mod in ob.modifiers :
For each modifiers ( of each objects)

if mod.type != "NODES" : continue
If the modifiers isn’t a geometry node modifier then do nothing

print(mod.node_group.name,'/' ,ob.name)
print the modifier’s node group name, and the object name.

It’s probably the “continue” thing that you’re not used to ?

hope that helps and fell free to ask if you have further questions !

how does this = Geom nodes instead of normal nodes ?

and the continue not certain what it does !

also how can we detect EEVEE nodes or cycles nodes?

thanks
happy bl

“Normal” (shading) nodes aren’t modifiers. This is only iterating over modifiers.

Again, this is a different situation. This script only applies to modifiers.

It does this: if the modifier isn’t a geometry nodes modifier, go to the next iteration of the loop (the next modifier, or next object if there are no more modifiers)

1 Like

ok so Nodes as a modifier’s name is a Geom node !

thanks
happy bl

1 Like

I’m not sure to get you completely, but it’s not using names to retrieve anything.
But it’s using the .type

Each datablock in blender has a type, for instance an object.type can be “MESH”, “CURVE”, “LATTICE”, “ARMATURE”…

And in our case, the modifiers .type is called “NODES”, that way it’s possible to know that it’s a geometry node modifiers.

In the same fashion you can inspect nodegroups in the .blend and filter those with the “GEOMETRY” type, which is another way to find geometry nodegroups.
But to my knowledge it’s not possible to retreive the users of these groups.

So if you wanted to get all the materials that uses one particular group, you need to inspect all the materials, rather than finding the group and query it’s users.

hope that helps !

i think i get the idea here using the modifier’s type

hope it won’t change too much later on in future

thanks
happy bl

I wrote this to give dependencies. Its not beautiful but gets the job done.

import bpy
from bpy import context



def report_gn_data():
    print('start')
    print('MESH TREES')
    print_mesh_trees()
   
    print('')
    print('MESH USERS:')
    mesh_usages = get_mesh_usages()
    
    for k,v in mesh_usages.items():
        print(k + ': ' + str(v))


    node_trees = get_all_geometry_node_trees()
    print('')
    print('NODE TREES')
    for t in node_trees:
        print_tree(t)
        print('')
    
    print('')
    print('NODE USERS')
    usages = {}
    for t in node_trees:        
        print(t.name + ': '+ str(t.users))
    

def print_mesh_trees():
    
    for obj in bpy.context.scene.objects:
        geoMods = []
        for mod in obj.modifiers:

           
            if mod.type == 'NODES':
                tree = mod.node_group
                
                if(tree == None):
                    continue
                
                geoMods.append(mod)
        
        if(len(geoMods)):
            print('obj: ' + obj.name)
            
            for mod in geoMods:
                print(' ━ mod: ' + mod.name)
                print_tree(tree, 2)
            print('')
        
def get_mesh_usages():
    usages = {}
    for obj in bpy.context.scene.objects:
        for mod in obj.modifiers:

            if mod.type == 'NODES':
                tree = mod.node_group
                
                if(tree == None):
                    continue
              
                usages = get_node_group_usages(tree, usages)
    
    return usages
                
        
def get_node_group_usages(tree, usages = {}):
    
    if(not tree.name in usages):        
        usages[tree.name] = 0
            
    usages[tree.name] += 1
     
    children = filter_array_type(tree.nodes, 'GeometryNodeGroup')
    
    for nodeGroup in children:
        tree = nodeGroup.node_tree
        usages = get_node_group_usages(tree, usages)
        
    return usages
    

def print_tree(tree, depth=1):

    children = filter_array_type(tree.nodes, 'GeometryNodeGroup')
    children = unique_node_list(children)  
    
    if(not len(children) and depth == 1):
        return
           
    if(depth == 1):
        print(((depth-1) * '.') + ' ━ ' + tree.name)        
    else:
        print(((depth-1) * '.') + '┗━ ' + tree.name)

    for c in children:
        print_tree(c.node_tree, depth + 1)    
    
    
def get_all_geometry_node_trees():
    
    return filter_array_type(bpy.data.node_groups, 'GeometryNodeTree')

def filter_array_type(array, objType):
    filtered = filter(lambda n: type(n).__name__ == objType, array)
    
    return list(filtered)

def unique_node_list(list):
     
    d = {}
    for i in list:
        if(i.node_tree != None):
            d[i.node_tree.name] = i
        
    
    unique = []
    for k,v in d.items():
       unique.append(v)
       
    return unique
    
report_gn_data()


1 Like

Thanks. It clearly shows that I have a few cases of duplication of nodes for example name.001 which I know should be the same.