探索物理模拟:从CMake到Euler方法

物理模拟项目中,经常需要处理复杂的数学模型和算法。本文将介绍如何通过CMake设置项目,实施测试驱动开发(TDD),并实现Euler方法来解决物理问题。

CMake项目设置

CMake是一个跨平台的自动化构建系统,它使用配置文件(称为CMakeLists.txt)来生成标准的构建文件。根据Reddit上的一些建议,对CMake配置进行了一些调整,但仍然不确定当前的项目结构是否能够持续到项目结束。如果错过了之前的帖子,可以在这里找到: 和 。

测试驱动开发(TDD)

为了实施TDD,首先需要定义一些测试案例,然后才能实现求解器库。一个简单的场景是两个具有等质量的点,距离为d,没有初始速度和加速度。显然,可以用它进行数百种不同的复杂测试。但想从两个非常简单和具体的测试开始,点质量只在x和y方向上加速和移动。这样,至少可以检查基础假设、公式和实现是否正确。

引力方程的异常

还需要考虑引力方程的异常,用它来计算加速度。当两个质量达到奇点,换句话说,碰撞时会发生什么?如果两个点在空间中处于同一位置,那么它们之间的距离r为零。如果发生这种情况,加速度F将变得无限大,因此两个点的加速度也会无限大。两个点将相互飞向虚空。

解决方案

基本上有两种方法可以解决这个问题。第一种解决方案是将两个质量合并为一个。每个黑洞的粉丝可能会同意这个解决方案,但有一个小问题,合并恒星会发生什么,完全不理解,因此对来说很难建模。显然,模型并不完美,有很多缺陷,但合并恒星的模型会使其复杂性爆炸。第二种解决方案相对简单,不允许质量碰撞。这可以通过向引力方程添加一个错误项来实现。此外,还需要添加一个符号项来获得正确的力的方向。

Euler方法的实现

现在有几种选择来实现所谓的显式和隐式方法,例如Euler、Euler-Cromer、Leapfrog、Verlet等。将从最简单的,但不太准确的显式Euler方法开始。

测试案例

让从一个名为“显式Euler算法与两点质量”的简单测试案例开始,在solverTest可执行文件中。

TEST_CASE( "Explicit euler algorithm with two point mass", "[euler]" ) { Solver solver; ParticleBuilder particleBuilder; Particle particleA = particleBuilder .position() .mass() .build(); Particle particleB = particleBuilder .position() .mass() .build(); std::vector<Particle> particles = { particleA, particleB }; const double EPSILON = 1e-3; SECTION( "Two still standing point mass are attracting each other" ) { std::vector<Particle> result = solver.solve(particles, EPSILON); REQUIRE(false); } }

这个测试案例描述了到目前为止粗糙的设计,希望如何使用求解器。需要一个求解器来执行计算,一个粒子向量的粒子,以及一个粒子构建器,它提供了一种流畅的接口,可以轻松地为生成粒子。Euler算法不仅需要粒子来计算,还需要一个时间步长,正如从第一个帖子中记得的,将在源代码中称之为EPSILON(不要与需要解决的奇异性混淆)。可以通过下载v0.2.0来获取这个起始状态。

测试结果

第一次测试将检查两个粒子在x方向测试中的以下结果:所有3个结果在纯x方向和y方向移动的点质量的情况下都是有效的。在实现了测试并使其编译之后,solverTest文件看起来像这样:

double Inverse(double value) { return -value; } Particle GenerateStandardParticle(double xPosition, double yPosition) { ParticleBuilder particleBuilder; return particleBuilder .position({xPosition, yPosition}) .mass(1e10) .build(); } TEST_CASE( "Explicit euler algorithm with two point mass", "[euler]" ) { Solver solver; const std::vector<Particle> particlesX = { GenerateStandardParticle(0.5, 0), GenerateStandardParticle(-0.5, 0)}; const std::vector<Particle> particlesY = { GenerateStandardParticle(0, 0.5), GenerateStandardParticle(0, -0.5)}; const double EPSILON = 1e-3; //Solution const double acceleration = -0.6674079993; //m/s^2 const double velocity = -0.06674079993; //m/s const double position = 0.48665184; //m SECTION( "Two still standing point mass are attracting each other in x-direction" ) { std::vector<Particle> result = solver.solve(particlesX, EPSILON); Particle &particle = result.front(); REQUIRE(particle.acceleration.x == Approx(acceleration)); REQUIRE(particle.velocity.x == Approx(velocity)); REQUIRE(particle.position.x == Approx(position)); particle = result.back(); REQUIRE(particle.acceleration.x == Approx(Inverse(acceleration))); REQUIRE(particle.velocity.x == Approx(Inverse(velocity))); REQUIRE(particle.position.x == Approx(Inverse(position))); } SECTION( "Two still standing point mass are attracting each other in y-direction" ) { std::vector<Particle> result = solver.solve(particlesY, EPSILON); Particle &particle = result.front(); REQUIRE(particle.acceleration.y == Approx(acceleration)); REQUIRE(particle.velocity.y == Approx(velocity)); REQUIRE(particle.position.y == Approx(position)); particle = result.back(); REQUIRE(particle.acceleration.y == Approx(Inverse(acceleration))); REQUIRE(particle.velocity.y == Approx(Inverse(velocity))); REQUIRE(particle.position.y == Approx(Inverse(position))); } }

让首先看看SECTION。执行两个测试,一个测试纯x方向上的加速度、速度和位置,另一个测试纯y方向上的加速度、速度和位置。两个测试都使用了Inverse()函数,该函数用于测试第二个粒子的预期结果。Catch2的Approx包装类重载了比较运算符,并提供了3种不同的配置方法。

粒子构建器

ParticleBuilder本身提供了一个流畅的接口,可以轻松配置和生成点质量。对于如此简单的案例,使用带有流畅接口的构建器模式可能没有必要,但将在后面的更高级生成中需要它。

class ParticleBuilder { public: ParticleBuilder() : mMass(0) {} ParticleBuilder & position(const Vector2D &position); ParticleBuilder & velocity(const Vector2D &velocity); ParticleBuilder & acceleration(const Vector2D &acceleration); ParticleBuilder & mass(double mass); Particle build() const; private: double mMass; Vector2D mAcceleration; Vector2D mVelocity; Vector2D mPosition; }; Vector2D是一个简单的数据结构,可能会在以后扩展更多功能。测试结果如预期那样失败。
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485