力场#

力场(force field)在每个子步为实体的粒子施加随空间变化的加速度。可用它让流体受风推动、把液体搅成旋涡、添加阻力或驱动湍流,而无需自己逐粒子编写作用力。

尽管名为力场,它实际上是一个加速度场:贡献的是以 m/s² 为单位的加速度,而不是以牛顿为单位的力。这里加速度才是具有物理意义的量,因为力没有空间密度的概念,而场在空间中每一点都返回一个值。Genesis World 按 vel += acc * substep_dt 把加速度积分到每个受影响粒子的速度中。

力场只作用于由 PBD 和 SPH 求解器推进的基于粒子的实体(流体、布料及其他粒子材质)。它们不影响刚体、MPM 或 FEM 实体。若要对刚体施加世界力,请改用其实体 API。

Note

该特性目前还没有随附的可运行示例,因此下面的片段仅作说明,不过其中每个符号和参数都来自实际 API(gs.force_fields)和 力场 API。

最小示例#

向场景添加一个场,激活它,构建,然后步进。下例中,恒定的侧风把一股下落的 SPH 液体吹向一侧:

import genesis as gs

gs.init(backend=gs.cpu)

scene = gs.Scene(
    sim_options=gs.options.SimOptions(dt=4e-3, substeps=10),
    sph_options=gs.options.SPHOptions(
        lower_bound=(-0.5, -0.5, 0.0),
        upper_bound=(0.5, 0.5, 1.0),
        particle_size=0.01,
    ),
    show_viewer=True,
)

scene.add_entity(gs.morphs.Plane())
liquid = scene.add_entity(
    material=gs.materials.SPH.Liquid(),
    morph=gs.morphs.Box(pos=(0.0, 0.0, 0.65), size=(0.4, 0.4, 0.4)),
)

# Instantiate, then register with the scene *before* building.
wind = gs.force_fields.Constant(direction=(1, 0, 0), strength=5.0)  # 5 m/s2 along +X
scene.add_force_field(wind)

scene.build()

wind.activate()  # fields start inactive; turn it on to apply acceleration
for i in range(1000):
    scene.step()

生命周期#

力场经历四个阶段。顺序很重要:在 build() 之后添加,或忘记激活,是最容易犯的两个错误。

  1. 实例化:从 gs.force_fields.* 创建场并传入其参数:

    wind = gs.force_fields.Constant(direction=(1, 0, 0), strength=5.0)
    
  2. 注册:用 scene.add_force_field(field) 将其注册到场景,该方法返回同一个场。这必须在 scene.build() 之前完成:已注册场的数量会被烘焙进编译后的求解器内核,因此之后添加的场永远不会被求值。

  3. 激活:用 field.activate() 激活。场在创建时处于非激活状态(field.active 为 False),在激活前贡献的加速度为零。构建后(包括仿真过程中)可随时切换 activate() 和 deactivate(),并读取 field.active 查看当前状态。

  4. 步进:推进场景。每个子步中,求解器会把激活场的加速度加到每个受影响的粒子上。

可用的场类型#

所有场都是 gs.force_fields.ForceField 的子类,返回以 m/s² 为单位的加速度。除非另有说明,正的 strength 会放大效果,向量遵循右手系、Z 轴向上的坐标系。

场

效果

关键参数

Constant

处处均匀的加速度,类似稳定的重力或风。

direction(归一化)、strength(m/s²)

Wind

恒定加速度,但仅限无限圆柱内部;外部为零。

direction(圆柱轴,归一化)、strength(m/s²)、radius(m)、center(m)

Point

按 strength 的符号把粒子吸引到某点或将其排斥,并随距离衰减。

strength(m/s²)、position(m)、falloff_pow、flow

Drag

与速度反向的加速度,由线性项和二次项组合。

linear(1/s)、quadratic(1/m)

Noise

每个粒子每一步独立随机加速度,各轴在 [-strength, strength] 内均匀分布。

strength(m/s²)

Vortex

绕轴旋转粒子,含切向和径向分量,并随距离衰减。

direction(轴)、center(m)、strength_perpendicular(+ 逆时针)、strength_radial(+ 向内)、falloff_pow、falloff_min(m)、falloff_max(m)、damping

Turbulence

从三维 Perlin 噪声采样的平滑变化加速度。

strength(m/s²)、frequency(空间)、flow、seed

Custom

你自己编写的加速度函数,参数为位置、速度、时间和粒子索引。

func

对于 Custom,func 是一个 Quadrants 内核函数(用 @qd.func 包装的 Python 可调用对象),签名为 f(pos, vel, t, i) -> vec3,返回以 m/s² 为单位的加速度:

import quadrants as qd


@qd.func
def swirl(pos, vel, t, i):
    return qd.Vector([-pos[1], pos[0], 0.0], dt=gs.qd_float)  # m/s2


scene.add_force_field(gs.force_fields.Custom(swirl))

Turbulence 由 gs.force_fields.PerlinNoiseField 构建,后者是一个独立的三维 Perlin 噪声生成器(参数为 wrap_size、frequency、seed、seed_offset)。Turbulence 覆盖了常见情形,因此只有需要相干噪声的 Custom 场才需要自行构建生成器。

注意事项与常见陷阱#

  • 它是加速度,不是力。 数值单位为 m/s²,与粒子质量无关,因此重粒子和轻粒子每一步获得的速度增量相同。若你要从力的角度思考,请自行乘以质量。

  • 只有 PBD 和 SPH 粒子受影响。 请添加基于粒子的实体(SPH 液体、PBD 布料或流体),场才有作用对象。

  • 场会叠加。 求解器对所有激活场求和,因此 Constant 重力偏移、Drag 项和 Turbulence 阵风会合并为一个加速度。

  • 激活可以随意切换。 因为激活只是翻转运行时标志,而不是重新编译内核,所以仿真中开关场不产生额外开销,也无需重新构建。

另请参阅#