排布质点的时候出现奇怪bug

我定义了一个put_particle函数,用来排布初始化时候的质点位置,奇怪的是,偶尔会出现排布的颗粒漏掉一个的情况,下面是源码以及同样的代码连续运行三次的结果,运行环境为win10+python3.7+Taichi 0.6.8:

import taichi as ti

ti.init(debug=True)

max_num_particles = 256

dt = 1e-3

bottom_y = 0.05

n = ti.var(ti.i32, shape=())
k = ti.var(ti.f32, shape=())
mass = 1

x = ti.Vector(2, dt=ti.f32, shape=max_num_particles)
v = ti.Vector(2, dt=ti.f32, shape=max_num_particles)
rest_len = ti.var(ti.f32, shape=(max_num_particles, max_num_particles))
gravity = [0, -9.8]

@ti.func
def new_particle(pos_x: ti.f32, pos_y: ti.f32): # Taichi doesn't support using Matrices as kernel arguments yet
    new_particle_id = n[None] # 这里不要忘了[None]
    x[new_particle_id] = [pos_x, pos_y]
    v[new_particle_id] = [0, 0]
    n[None] += 1

@ti.kernel
def put_particles(m: ti.i32, n: ti.i32):
    x_start = 0.3
    y_start = 0.3
    dx = 0.1
    for i in range(m):
        for j in range(n):
            new_particle(0.3+j*0.1, 0.3+i*0.1)

@ti.kernel
def substep():
    for i in range(n[None]): # 这里不要忘了[None]
        force = mass * ti.Vector(gravity)
        v[i] += force / mass * dt

        if x[i].y + v[i].y * dt <= bottom_y:
            x[i].y = bottom_y
            v[i].y = 0

        x[i] += dt * v[i]

gui = ti.GUI('Mass Spring System', res=(512, 512), background_color=0xdddddd)

# k[None] = 10000

put_particles(4, 3) # 所有操作都在gui生成之后

while True:
    for e in gui.get_events(ti.GUI.PRESS):
        if e.key in [ti.GUI.ESCAPE, ti.GUI.EXIT]:
            exit()
        # elif e.key == ti.GUI.LMB:
        #     new_particle(e.pos[0], e.pos[1])
    # for i in range(10):
    #     substep()
        
    X = x.to_numpy()
    gui.circles(X[:n[None]], color=0xffaa77, radius=5)


    gui.show()

运行第一次结果:
image

运行第二次结果:
image

运行第三次结果:
image

怀疑是 put_particles 自动并行的问题。

new_particle_id = n[None]n[None] += 1 这两步操作合在一起不是原子操作。
可能大家都读的是同一个值,然后重复给一个点赋值,后面再一起+1,导致重复赋值和跳过某些点。

打印了下 X[:n[None]] 出问题的时候,确实有的点没有赋值

最简单的方法就是把 new_particleput_particles 不加装饰器,当作普通函数就行了。

原来如此,谢谢解答!

建议改成ti.atomic_add

@ti.func
def new_particle(pos): # Taichi does support using Matrices as **function** arguments, just don't type-hint
    new_particle_id = ti.atomic_add(n[None], 1)  # return the old value of `n[None]`
    x[new_particle_id] = pos
    v[new_particle_id] = [0, 0]
2 个赞

多谢大佬建议