在c++中向粒子添加力的列表

Adding a list of forces to a particle in c++

本文关键字:列表 添加 粒子 c++      更新时间:2023-10-16

我对c++有点陌生,如果这是一个愚蠢的问题,我很抱歉。

我有一个结构,它表示粒子系统中的粒子。除了位置、速度和质量等标准的东西,我想给它一个力的列表,这样每个力都是一个函数,我把粒子传给它,根据粒子的当前状态(是否),这个函数返回一个力向量。理想情况下,我会把每个这样的力向量的结果相加,得到一个净力,然后我可以用它来计算下一次滴答声中粒子的速度。

这就是我希望我的粒子看起来像

struct particle {
double mass;
// position
double x, y, z;
// velocity
double dx, dy, dz;

std::list<function> forces;
};

现在我的问题是:我能在不实现通用力基类的情况下做到这一点吗?该基类实现了计算力的函数?有没有一种方法可以指定一个具有相同调用签名的函数列表?

如果可以保证所有函数都具有相同的方法签名,那么可以使用模板化函数[cppreference.com]类。我修改了你的样品以说明如何使用。

#include <functional>
#include <list>
#include <cmath>
using namespace std;
struct particle 
{
double mass;
// position
double x, y, z;
// velocity
double dx, dy, dz;
// A list of forces that take a particle and return a double
// The space between the two > symbols is needed in pre-c++11 compilers.
list<function<double(const particle&)> > forces;
};
// An example function to calculate the force due to gravity.
double gravity(const particle& p)
{
return p.mass * -9.8;
}
// Making something up for air resistance
double resistance(const particle& p)
{
return 0.1 * sqrt(p.dx * p.dx + p.dy * p.dy + p.dz * p.dz);
}
int main()
{
particle p;
p.mass = 10;
p.x = 0;
p.y = 100;
p.z = 0;
p.dx = 0;
p.dy = 0;
p.dz = 0;
p.forces.push_back(gravity);
p.forces.push_back(resistance);
}

如果你要处理三维力,你可能需要返回类型中的更多信息,而不仅仅是二重,但这应该是一个很好的起点。此外,如果您有一个兼容c++11的编译器,您可能还想查找lambda函数,以便可以在同一行中创建这些函数。

您可以使用std::函数。类似这样的东西:

// define a type for the function
typedef std::function< void(particle const&, float *fxfyfz) > forcer_function;
std::vector< forcer_function > forces;

现在,我来谈谈表演。由于这是你所说的粒子,我假设你有相当多的粒子(例如至少几百个)。因此,我假设您有兴趣让这些代码以适度的速度运行。

因此,首先,由于缓存属性不好,不建议对容器使用std::list。这将使您的代码慢得多。因此,使用std::vector。

其次,将力列表添加为粒子结构的成员是不常见的。你真的想对每个粒子使用不同的力吗?典型地,存在<5个力和>100-1000个粒子。如果可以对所有粒子使用相同的力集合,那么将力移出粒子类将获得增益。例如,

struct particle
{
double mass;
// position
double x, y, z;
// velocity
double dx, dy, dz;
};
struct particle_container
{
std::vector< particle > particles;
std::vector< forcer_function > forces;
void update();
};
void particle_container::update()
{
for(particle &p : particles) {
double rx, ry, rz;
rx = ry = rz = 0.0;
for(forcer_function fn : forces) {
double f[3];
fn(p, &f[0]);
rx += f[0];
ry += f[1];
rz += f[2];
}
// integrate resulting force, etc
// ...
}
}

如果OTOH你真的想使用每粒子的力,你仍然可以使用我上面概述的方法,通过在不同的容器对象中对具有相同力集合的粒子进行分组。然后,您可以重用以上所有内容,再添加一个类将修复它:

struct particle_groups
{
std::vector< particle_container > groups;
void update();
};
void particle_groups::update()
{
for(auto &g : groups) {
g.update();
}
};

如果你真的,真的,不想分组,那么至少要考虑是否有一种方法可以使用粒子成员来消除不活动的力。那么你仍然可以使用上面的方法。例如,像这样:

struct particle
{
double mass;
// position
double x, y, z;
// velocity
double dx, dy, dz;
// is gravity active? either 1.0 or 0.0
double grav;
// is player interaction active? either 1.0 or 0.0
double player;
// etc... for all possible forces
};

然后只需将结果重力乘以粒子的grav成员,就可以根据particle.grav的值是1.0还是0.0有效地关闭或打开该粒子的重力。

最后,std::函数很慢。您可以混合使用上述两种方法,并使用单个函数。像这样:

struct particle
{
double mass;
// position
double x, y, z;
// velocity
double dx, dy, dz;
};
struct force_settings
{
double grav;
double attractor;
double player;
//etc...
};
struct particle_container
{
// no need to keep pointers to functions
force_settings forces;
std::vector< particle > particles;
void update();
void compute_forces(particle const& p, double *rf) const
{
// zero resulting force
rf[0] = rf[1] = rf[2] = 0.0;
// compute gravity, (assume y axis)
rf[1] += forces.grav * 9.8; // will be either 9.8 or 0.0
// compute attractor
double ax = p.x - attractor.x;
double ay = p.y - attractor.y;
double az = p.z - attractor.z;
rf[0] += forces.attraction * ax*ax;
rf[1] += forces.attraction * ay*ay;
rf[2] += forces.attraction * az*az;
// etc... more forces here
}
};
void particle_container::update()
{
for(particle &p : particles) {
double rf[3];
compute_forces(p, &rf);
// integrate, etc...
}
}