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()