【问题标题】:Adding a list of forces to a particle in c++在c ++中向粒子添加力列表
【发布时间】:2016-05-24 00:11:49
【问题描述】:

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

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

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

struct particle {
    double mass;

    // position
    double x, y, z;

    // velocity
    double dx, dy, dz;


    std::list<function> forces;
};

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

【问题讨论】:

  • 从性能的角度来看,您肯定想要std::vector 而不是std::list
  • 有什么原因吗?如果我只是在每个滴答声中按顺序迭代力,会有所不同吗?
  • vector 上的迭代要快一些,因为它的元素在内存中占据了一个连续的区域,而且你不需要读取指针并跟随它。
  • @chrisnakovski 迭代向量不仅快一点,而且快得多。列表中的每一次跳跃都是现金失误(通常)。矢量与预取器配合得很好。
  • 你应该添加c++11标签,因为c++98没有std::function

标签: c++ c++11 particles particle-system


【解决方案1】:

如果您可以保证所有函数都具有相同的方法签名,那么您可以使用模板化的function[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 functions 以便可以在同一行中创建函数。

【讨论】:

  • 感谢@user31264。函数类在c++11之前是不存在的,但是可以用function pointers代替。
  • 这正是我想要的。谢谢!
【解决方案2】:

您可以使用 std::function。像这样的:

// 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。

其次,将力列表添加为粒子结构的成员并不常见。你真的想对每个粒子使用不同的力吗?通常,有 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
};

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

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

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...
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    • 2020-09-30
    相关资源
    最近更新 更多