[SOLVED] Help for Remove particle systems

Hi guys. I have a very simple piece of code, but it is giving me problems. I have a scene with some objects with particles, and I’m trying to remove only the particles that have in the name the word “Dust”, When there is only one particle slot it does it fine but if I have several particle slots it removes the particle modifiers randomly. If someone knows what I can do or someone can help me, thanks.

Here is the code:

import bpy

for obj in bpy.context.selected_objects:
    for mod in obj.modifiers: 
        if "Dust" in mod.name:
            obj.modifiers.remove(mod)

I am also trying not to use the bpy.ops.object.particle_system_remove operator because it is a situation with many objects and it takes too long to use the operator inside a loop.

Before:
after

After:
before

1 Like

Got a .blend demonstrating the problem? Quick testing does not show an issue

1 Like

yes, no problem.
https://pasteall.org/blend/1cb78589370f45d08da80ef13261294a

1 Like

Your code checks the name of the modifier, but the names you’re looking at in the UI are the particle system names. Hence, you need to check mod.particle_system.name in your code if you want to base the decision on the name of the particle systems. Additionally, you are currently only removing the modifier not the particle system itself. Depending on what your goal is you may want to add this as well.

With the following code the result is the same, since my modifiers are called the same as the particle systems, I don’t care for the moment if the particle systems are orphaned.

for obj in bpy.context.selected_objects:
    for mod in obj.modifiers: 
        if "Dust" in mod.particle_system.name:
            obj.modifiers.remove(mod)

With modifiers.remove it is deleting random, since it deletes a Debris and a Dust, when it should never delete the Debris. And does not delete all Dust.

I’m not sure if it’s a bug.

Moreover, they correspond because I activate and deactivate the visibility of the particle systems and they are activated and deactivated in the modifiers, so the names correspond well.

Did you tried bpy.ops.object.modifier_remove(modifier=mod_name) ?
Do it still produce your “bug”?

While searching in my old plugin code i used this instead of the more “classic” method so i suppose that i also encountered this issue in the past

The behavior you’re describing sounds like your code causes undefined behavior (see Gotchas). Is the actual script larger? If yes, then test if this problem happens when only running the snippet. If no, then there may be an issue, because you are iterating over the modifiers while removing them. I don’t have time to check this at the moment though.

Yes, as I commented in the first post, with the operator it works fine, but I’m trying not to use the particle_system_remove operator (with modifier_remove I haven’t tried) in a loop because when I have scenes with many passes in the loop using operators penalizes a lot the performance and it’s very very slow. That’s why I’m trying to do it with modifier.remove and not with operators inside a loop. If it is a bug it would be of the remove of modifiers not of the operators.

Yes, I am only using the fragment I have given as an example, the problem remains. I think it could be that the list of modifiers is changing on the fly in each pass and that’s why it is deleting random, but I can’t think how I could solve that. Can I ask the indexes and tell remove to delete by index? maybe asking the index at each pass if the list changes would be enough but I don’t think it is possible.

aha my old self left this piece of comment

     for m in m_to_remove:
            # Bug Here?
            # wtf, forced to set active index of particle system due to some random behavior, 
            # modifier_remove() and modifiers.remove() will remove only active particle index for some extremely weird & random reasons. 

            idx = [i for i,ps in enumerate(bpy.context.object.particle_systems) if ps.name == m.name][0]
            A.particle_systems.active_index = idx
            bpy.ops.object.modifier_remove(modifier=m.name)

so it seem that setting a particle system active first was required
Bug indeed

*edit: at first glance this comprehension list i wrote to find the idx looks a bit dirty you might not want to copy paste this part

2 Likes

Thanks, I will try it!

Thank you very much, this worked!!

for obj in bpy.context.selected_objects:
    for mod in obj.modifiers:
        if "Dust" in mod.name:
            match = False
            i = 0
            idx = -1
            while match == False and i < len(obj.particle_systems):
                if "Dust" in obj.particle_systems[i].name:
                    idx = i
                    match = True
                i += 1
            if idx >= 0:
                obj.particle_systems.active_index = idx
                obj.modifiers.remove(mod)
3 Likes

ok so for noobs like me:
doing

for obj in bpy.context.selected_objects:
    obj.particle_systems.active_index = 0
    while obj.particle_systems.active_index < len(obj.particle_systems):
        if "Dust" in obj.particle_systems.active.name:
            bpy.ops.object.particle_system_remove()
        else:
            obj.particle_systems.active_index += 1

is the most simple solution, but on a lot of particles systems like 100,
doing:

for i in range(100):
    bpy.ops.object.particle_system_add()
    bpy.context.object.particle_systems[-1].name = "Dust"
    bpy.context.object.modifiers[-1].name = "Dust"

my memory is then 55MB
it takes more than 10s to remove all, memory ~30MB

so I did

for obj in bpy.context.selected_objects:
    obj.particle_systems.active_index = 0
    while obj.particle_systems.active_index < len(obj.particle_systems):
        part_name = obj.particle_systems.active.name
        if "Dust" in part_name:
            for mod in obj.modifiers:
                if part_name == mod.name:
                    obj.modifiers.remove(mod)
        else:
            obj.particle_systems.active_index += 1

time 0s, memory after ~30MB ,

so I tried on 200, memory 81MB
time 0.004s memory ~30MB

and with the solution of the previous comment
time still 0 memory ~30MB

my solution is doing a double loop…
but with the first example the time difference is really huge, I wonder why?
when removing the modifier this is removing the particle_systems and vice versa, and the memory after is quite the same

As far as I understand, every time you use operators (bpy.ops.xxx.xxx) inside a loop, it always takes a very long times, and if there are alternatives, it is strongly recommended not to use operators inside a loop. I’m not sure why this happens, but it does. The memory issue you always get the same thing because surely the particles stay in to the blender limbo, Orphaned elements you can see them in the Outliner > Orphan Data.

I’ve looked into Blender’s code to see why the active particle system is used. It explicitly does so by calling object_remove_particle_system from object_modifier_remove which uses ParticleSystem *psys = psys_get_current(ob); to get the particle system.

I’ve implemented a quick test patch and contacted Bastien Montagne, in case there are edge cases that I’m not aware of why this might have been done.

4 Likes

Oh wow, thank you so much @Robert!!

Happy to see this bug getting some attention :clap:
but won’t all this be obsolete very soon?

yes I did the same and I found particles_systems was checking operators via this psys (item) so I was surprised to find a difference of time.
@ zebus I forgot to do a remove orphan to see

The patch is now in master. In the next daily build it should work as expected.

4 Likes