MccStepper:批量碰撞步进器
Tip
AoS / SoA 是存储排法:AoS(Array of Structures)按粒子存放 {x,v,…};SoA(Structure of Arrays)分别存放 x[]、v[] 等字段数组。物理模型相同,适配器负责用统一接口访问这些数据。
ParticleAdapter(粒子适配器):把主程序已有的粒子存储接到 G02 的“转换接头”,负责读取粒子和提交更新,不是额外的物理粒子。
workspace(工作区)是反复使用的临时内存。跨时间步复用可减少分配,但同一对象不能同时处理两个调用。
Tip
cell 与 state 的特殊值
cell 是网格编号,不是数组下标。背景的 UINT32_MAX``表示全域备用背景;正常网格编号必须与粒子一致。
``invalid_state 是 C++ 的 65535 哨兵值,它只供按物种合并的背景使用,不等于基态,也不代表自动分配各态密度。
入门时用 state_id(species, "ground") 查询真实态;名称须存在于模型。被跟踪粒子的态必须有效。
Tip
可直接构建的最小调用程序
以下程序已给出必要声明和变量来源。运行参数为 BUILD_DIR/generated_models/collision_box_elastic,不要换成不含 A/B 的模型。
// SPDX-License-Identifier: Apache-2.0
#include "mcc.hpp"
#include <iostream>
using namespace algoplasma::mcc;
int main(int argc, char** argv) {
if (argc != 2) return 2; // argv[1]: model directory
try {
MccEngine engine = MccEngine::load(argv[1]);
StepOptions options;
options.backend = CpuBackend::Serial;
MccStepper stepper(engine, options);
std::vector<ParticleState> particles(1);
particles[0].species = engine.species_id("A");
particles[0].state = engine.state_id(particles[0].species, "ground");
particles[0].id = 1;
particles[0].cell = 0;
particles[0].velocity = {1000.0, 0.0, 0.0};
std::vector<CellBackground> background(1);
background[0].cell = 0; // local cell; UINT32_MAX means uniform fallback
auto& gas = background[0].component;
gas.species = engine.species_id("B");
gas.state = engine.state_id(gas.species, "ground");
gas.density_m3 = 1.0e20;
gas.temperature_k = 300.0;
VectorParticleAdapter adapter(particles); // preserve across steps
MccWorkspace workspace; // preserve across steps
const Real dt_s = 1.0e-7;
const std::uint64_t seed = 1234;
// Two overloads, shown on different steps; never advance one step twice.
auto first = stepper.step(adapter, background, dt_s, 0, seed, workspace);
auto second = stepper.step(adapter, background, dt_s, 1, seed);
std::cout << "PASS " << first.real_events + second.real_events << '\n';
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
return 1;
}
}
实现文件:fun_G02_mcc_batch.cpp。下面保留对应 C++ 接口的实际名称,方便对照调用。
直接用途
C++ 的批量入口位于 batch.hpp,命名空间为 algoplasma::mcc。
它通过 ParticleAdapter 访问主程序粒子容器,成功后统一写回。
调用图见 架构说明。
[[nodiscard]] StepReport step(ParticleAdapter& particles,
const std::vector<CellBackground>& background,
Real dt_s, std::uint64_t global_step,
std::uint64_t seed) const;
[[nodiscard]] StepReport step(ParticleAdapter& particles,
const std::vector<CellBackground>& background,
Real dt_s, std::uint64_t global_step,
std::uint64_t seed, MccWorkspace& workspace) const;
上面是 MccStepper 类中的真实声明。五参数版本使用临时工作区;六参数版本复用调用者的工作区。
同一物理时间步只选一个版本调用;第六个参数不是线程数,也不是 StepOptions。
省略最后的 workspace 参数也可以,接口会使用一次性的工作区。
参数 |
方向 |
约定 |
|---|---|---|
particles |
inout |
ParticleAdapter 引用;成功后更新其管理的容器。 |
background |
in |
const vector<CellBackground>&;本次调用期间保持有效。 |
dt_s / global_step / seed |
in |
秒、全局步编号、随机种子;dt 为有限非负数。 |
workspace |
inout,可选重载 |
临时内存跨步复用;同一对象不能同时用于两个活动调用。 |
返回 StepReport |
out |
统计、背景收支、账本和分段耗时;异常时不返回成功报告。 |
StepOptions
默认 backend=Auto,线程数 threads=0,min_parallel_particles=4096, max_candidates_per_particle=1000000,record_events=false,exact_pruning=true, sampler=Prefix。OpenMP 需要构建时启用;设置 record_events 才会保留逐事件明细。 exact_pruning 根据当前背景条件收紧可用通道的上界,减少额外候选。
自定义粒子容器的接入
使用 vector<ParticleState> 时直接用 VectorParticleAdapter。 按物种分别存放各个字段的 ParticleBank/ParticleSoA 使用 SoaParticleAdapter。 自定义容器实现以下约定:
size、alive、particle 提供只读访问;next_id_hint 返回新 ID 的起点提示。
begin_step 刷新索引,不改变当前可见粒子。
prepare_commit 可分配内存并失败,但不能提前改动可见粒子。
commit 必须不失败,将已准备好的更新一次性写回。
适配器或 ID 高水位需要跨步保留,防止删除粒子后重新使用旧 ID。 主程序直接改动容器后,下次 begin_step 会重建必要索引。
prepare_step 的进阶用途
prepare_step 与带 workspace 的 step 接收相同输入,返回 PreparedMccStep。 它还未提交粒子。report 读暂存统计,largest_id 和 birth_keys 用于分配子粒子 ID, prepare_commit 准备存储,commit 写回,take_report 取走报告。 对象不可复制、可以移动;销毁会释放它对工作区的占用。 普通主程序直接调用 step 即可,MPI 包装使用分阶段接口协调多进程提交。
完整 C++ 调用程序
Tip
AoS (Array of Structures) stores one record per particle; SoA (Structure of Arrays) stores separate arrays for fields such as x[] and v[]. Adapters expose either layout through one interface; the physical model is unchanged.
ParticleAdapter connects host-owned storage to G02, exposing particle reads and update commits. It is an interface, not another physical particle.
A workspace is reusable scratch memory. Reuse reduces allocation, but one instance must not serve concurrent calls.
ABI (Application Binary Interface) defines how compiled code passes arguments and results. A handle refers to a library-owned object; use API functions rather than inspecting its internals.
Reservoir deltas track background gains/losses of particles, momentum and energy. A conservation ledger compares totals before and after reactions. Do not apply both as background source terms.
Tip
Special cell and state values
cell is a mesh ID, not an array index. Background UINT32_MAX selects uniform fallback; local IDs must match particles.
C++ invalid_state is the 65535 sentinel, only for species-aggregate backgrounds. It is not ground state and does not distribute density over states.
Beginners can look up a real state with state_id(species, "ground"); the label must exist in the model. Tracked particles must have valid states.
Tip
Complete buildable caller
Declarations and input initialization are included. Pass BUILD_DIR/generated_models/collision_box_elastic; the caller requires its A/B species.
// SPDX-License-Identifier: Apache-2.0
#include "mcc.hpp"
#include <iostream>
using namespace algoplasma::mcc;
int main(int argc, char** argv) {
if (argc != 2) return 2; // argv[1]: model directory
try {
MccEngine engine = MccEngine::load(argv[1]);
StepOptions options;
options.backend = CpuBackend::Serial;
MccStepper stepper(engine, options);
std::vector<ParticleState> particles(1);
particles[0].species = engine.species_id("A");
particles[0].state = engine.state_id(particles[0].species, "ground");
particles[0].id = 1;
particles[0].cell = 0;
particles[0].velocity = {1000.0, 0.0, 0.0};
std::vector<CellBackground> background(1);
background[0].cell = 0; // local cell; UINT32_MAX means uniform fallback
auto& gas = background[0].component;
gas.species = engine.species_id("B");
gas.state = engine.state_id(gas.species, "ground");
gas.density_m3 = 1.0e20;
gas.temperature_k = 300.0;
VectorParticleAdapter adapter(particles); // preserve across steps
MccWorkspace workspace; // preserve across steps
const Real dt_s = 1.0e-7;
const std::uint64_t seed = 1234;
// Two overloads, shown on different steps; never advance one step twice.
auto first = stepper.step(adapter, background, dt_s, 0, seed, workspace);
auto second = stepper.step(adapter, background, dt_s, 1, seed);
std::cout << "PASS " << first.real_events + second.real_events << '\n';
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
return 1;
}
}
Implementation: fun_G02_mcc_batch.cpp. The C++ interface names below match the callable API.
Direct purpose
The C++ batch entry in batch.hpp uses a ParticleAdapter to access host-owned particles and commit together. See Architecture.
[[nodiscard]] StepReport step(ParticleAdapter& particles,
const std::vector<CellBackground>& background,
Real dt_s, std::uint64_t global_step,
std::uint64_t seed) const;
[[nodiscard]] StepReport step(ParticleAdapter& particles,
const std::vector<CellBackground>& background,
Real dt_s, std::uint64_t global_step,
std::uint64_t seed, MccWorkspace& workspace) const;
These are the declarations inside MccStepper. Five arguments use temporary storage; six reuse caller storage. Choose one overload per physical step; the sixth argument is neither a thread count nor StepOptions. The overload without workspace uses temporary storage.
Argument |
Direction |
Contract |
|---|---|---|
particles |
inout |
ParticleAdapter reference; commits its container on success. |
background |
in |
const vector<CellBackground>&, valid throughout the call. |
dt_s / global_step / seed |
in |
Finite nonnegative seconds, global step and random seed. |
workspace |
optional overload, inout |
Reusable scratch storage, not shared by active calls. |
StepReport return |
out |
Counts, exchange, ledger and timing; failure throws. |
Options and storage
StepOptions defaults: Auto backend, threads=0, min_parallel_particles=4096, max_candidates_per_particle=1000000, record_events=false, exact_pruning=true, Prefix sampler. OpenMP requires build support. record_events enables event details. exact_pruning tightens channel bounds using current background conditions.
VectorParticleAdapter handles vector storage. SoaParticleAdapter handles ParticleBank/ParticleSoA. Custom adapters implement size/alive/particle/next_id_hint, refresh indices without changing particles in begin_step, prepare storage without visible mutation in prepare_commit, and provide non-failing commit. Preserve the adapter or ID high-water mark across steps to avoid reusing deleted IDs.
Staged preparation
prepare_step takes the workspace overload’s inputs and returns a movable, noncopyable PreparedMccStep. report inspects staged diagnostics; largest_id/birth_keys support child-ID assignment; prepare_commit stages storage, commit writes it, and take_report transfers the report. Destruction releases workspace occupancy. Most hosts use step directly; MPI uses the stages to coordinate commit.
Complete C++ caller
1// SPDX-License-Identifier: Apache-2.0
2#include "mcc.hpp"
3
4#include <cstdint>
5#include <iostream>
6#include <stdexcept>
7#include <vector>
8
9using namespace algoplasma::mcc;
10
11int main(int argc, char** argv) {
12 if (argc != 2) throw std::runtime_error("usage: g02_mcc_host_pic MODEL_DIR");
13 const MccEngine engine = MccEngine::load(argv[1]);
14 StepOptions options;
15 options.backend = CpuBackend::Auto;
16 const MccStepper stepper(engine, options);
17 MccWorkspace workspace;
18
19 std::vector<ParticleState> particles(1);
20 particles[0].species = engine.species_id("e-");
21 particles[0].state = engine.state_id(particles[0].species, "ground");
22 particles[0].velocity = {2.0e6, 0.0, 0.0};
23 particles[0].id = 1;
24 VectorParticleAdapter adapter(particles);
25
26 std::vector<CellBackground> background;
27 for (const Species& species : engine.model().species.all()) {
28 if (species.representation != Representation::Background) continue;
29 BackgroundComponent component;
30 component.species = species.id;
31 component.state = engine.state_id(species.id, "ground");
32 component.density_m3 = species.name == "M" ? 2.0e20 : 0.0;
33 component.temperature_k = 300.0;
34 background.push_back({UINT32_MAX, component});
35 }
36
37 for (std::uint64_t step = 0; step < 3; ++step) {
38 const StepReport result = stepper.step(adapter, background, 1.0e-8, step, 1234,
39 workspace);
40 std::cout << "step=" << step << " particles=" << particles.size()
41 << " candidates=" << result.candidates
42 << " real=" << result.real_events << '\n';
43 // The host applies result.reservoir to its fluid/background solver here.
44 }
45}
文件归属 / File location
G_Collision/G02_MCC_network/fun_G02_mcc_batch.cpp