How to use python scripting method to generate “ParticleSet“ for each cube?

Isaac Sim Version

4.2.0

Operating System

Windows 11

GPU Information

    • Model: RTX 4080 Laptop

Topic Description

Detailed Description

How to use python scripting method to generate “ParticleSet“ cube by cube?
(I know how to use GUI to do it, but I need the python scripting code to do the same thing)
[related topic is: How to transform GUI action into python scripting format]
I use python scripting (as .py code uploaded) method to generate a cube. When I add Particle Sampler on the cube, system will automatically generate “ParticleSet“, I don’t need to generate it in code, and because I want that each cube has its own ParticleSet, not all of cubes share one ParticleSet.

But current result was: I can generate two cubes by python scripting with its own ParticleSystem, but two cube shares one ParticleSet. I need two ParticleSet.

My final goal is that I can use python scripting to simulate two melting cube with different melting speed controlled by different PBD material parameters. And the relationship is that cube is bounded by ParticleSet, ParticleSet is bounded by ParticleSystem, and ParticleSystem is bounded by PBD material. That’s why I need different ParticleSet to control different cube.

Screenshots or Videos

Additional Information

run_change_behavior_gimini_0917_v4.zip (2.4 KB)

Hi,
By default, when you creating the particle sampler, it will create one ParticleSet for you and share it for future samplers. You can duplicate it by right click on it → Duplicate, or you can just assign a new ParticleSet with a different prim name by editing your cube, it will be created automatically for you. As shown in following:

If you want to do this with Python, here’s an example for your reference.

from omni.isaac.kit import SimulationApp
simulation_app = SimulationApp({"headless": False})

from isaacsim.core.api import World
from isaacsim.core.api.objects import DynamicCuboid
from omni.physx.scripts import particleUtils
from omni.physx import acquire_physx_interface
from pxr import Sdf, Gf, Vt

import numpy as np

# Setup CPU sim for clarity, adjust as needed
world = World(backend="torch", device="cpu")
physx_interface = acquire_physx_interface()
physx_interface.overwrite_gpu_setting(1)

world.scene.add_default_ground_plane()

# =========================
# Define parameters for cubes
cube_settings = [
    {
        "prim_path": "/World/cube1",
        "position": [0.0, 0.0, 1.0],
        "color": [1.0, 0.2, 0.2],
        "particle_system_path": "/World/particleSystem1",
        "particleset_path": "/World/particles1",
        "melting_speed": 0.7,  # Use in PBD parameter
        "solver_position_iterations": 4,
    },
    {
        "prim_path": "/World/cube2",
        "position": [1.2, 0.0, 1.0],
        "color": [0.2, 0.2, 1.0],
        "particle_system_path": "/World/particleSystem2",
        "particleset_path": "/World/particles2",
        "melting_speed": 0.3,
        "solver_position_iterations": 10,
    }
]

scene = world.scene
stage = scene.stage
cube_scale = np.array([0.5, 0.5, 0.5])
particleSpacing = 0.05
numParticlesZ = 10

for cfg in cube_settings:
    # Add cube
    cube = scene.add(
        DynamicCuboid(
            prim_path=cfg["prim_path"],
            name=cfg["prim_path"].split("/")[-1],
            position=np.array(cfg["position"]),
            scale=cube_scale,
            color=np.array(cfg["color"]),
        )
    )
    system_path = Sdf.Path(cfg["particle_system_path"])
    set_path = Sdf.Path(cfg["particleset_path"])

    particle_system = particleUtils.add_physx_particle_system(
        stage=stage,
        particle_system_path=system_path,
        simulation_owner="physicsScene",
        # adjust parameter according to melting
        contact_offset=particleSpacing * 2.0,
        rest_offset=particleSpacing * 1.5,
        particle_contact_offset=particleSpacing * 1.51,
        solid_rest_offset=0.0,
        fluid_rest_offset=particleSpacing,
        solver_position_iterations=cfg["solver_position_iterations"],
    )

    # Define start of particle grid for this cube
    lower = Gf.Vec3f(cfg["position"][0] - 0.2, cfg["position"][1] - 0.2, cfg["position"][2] + 0.3)
    positions, velocities = particleUtils.create_particles_grid(
        lower, particleSpacing + 0.01, 8, 8, numParticlesZ
    )
    # Small randomization
    uniform_range = particleSpacing * 0.2
    rng = np.random.default_rng(42)
    for i in range(len(positions)):
        positions[i][0] += rng.uniform(-uniform_range, uniform_range)
        positions[i][1] += rng.uniform(-uniform_range, uniform_range)
        positions[i][2] += rng.uniform(-uniform_range, uniform_range)
    widths = [particleSpacing] * len(positions)
    # Particle set
    particleUtils.add_physx_particleset_points(
        stage=stage,
        path=set_path,
        positions_list=Vt.Vec3fArray(positions),
        velocities_list=Vt.Vec3fArray(velocities),
        widths_list=widths,
        particle_system_path=system_path,
        self_collision=True,
        fluid=False,
        particle_group=0,
        particle_mass=0.001,
        density=0.0,
    )

world.reset()
while simulation_app.is_running():
    world.step(render=True)

simulation_app.close()