我想在一个封闭的盒子里模拟一个点质量。没有摩擦,点质量遵守影响规律。因此,盒子的墙壁只有弹性碰撞。程序的输出是时间,位置(rx,ry,rz)和速度(vx,vy,vz)。我使用GNUplot绘制轨迹。
我现在遇到的问题是,点质量从某个地方获得能量。所以他们的跳跃每次都会变得更加强烈。
有人能检查我的代码吗?
/* Start of the code */
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
struct pointmass
{
double m; // mass
double r[3]; // coordinates
double v[3]; // velocity
};
// Grav.constant
const double G[3] = {0, -9.81, 0};
int main()
{
int Time = 0; // Duration
double Dt = 0; // Time steps
pointmass p0;
cerr << "Duration: ";
cin >> Time;
cerr << "Time steps: ";
cin >> Dt;
cerr << "Velocity of the point mass (vx,vy,vz)? ";
cin >> p0.v[0];
cin >> p0.v[1];
cin >> p0.v[2];
cerr << "Initial position of the point mass (x,y,z)? ";
cin >> p0.r[0];
cin >> p0.r[1];
cin >> p0.r[2];
for (double i = 0; i<Time; i+=Dt)
{
cout << i << setw(10);
for (int j = 0; j<=2; j++)
{
////////////position and velocity///////////
p0.r[j] = p0.r[j] + p0.v[j]*i + 0.5*G[j]*i*i;
p0.v[j] = p0.v[j] + G[j]*i;
///////////////////reflection/////////////////
if(p0.r[j] >= 250)
{
p0.r[j] = 500 - p0.r[j];
p0.v[j] = -p0.v[j];
}
else if(p0.r[j] <= 0)
{
p0.r[j] = -p0.r[j];
p0.v[j] = -p0.v[j];
}
//////////////////////////////////////////////
}
/////////////////////Output//////////////////
for(int j = 0; j<=2; j++)
{
cout << p0.r[j] << setw(10);
}
for(int j = 0; j<=2; j++)
{
cout << p0.v[j] << setw(10);
}
///////////////////////////////////////////////
cout << endl;
}
}
答案 0 :(得分:1)
我对其他评论者的时间循环有点担心 - 确保它代表时间的增量,而不是增长的持续时间。
不过,我认为主要的问题是你正在改变速度的所有三个组成部分的符号 在反思上。
在边界处,这与物理定律 - 线性动量和能量守恒 - 不一致。
要看到这一点,请考虑一下,如果你的粒子只在x-y平面内移动(z中的速度为零),并且在x = L时即将击中墙壁。
碰撞看起来像这样:
由墙壁施加在点质量上的力垂直于墙壁作用。因此,平行于墙壁的粒子的动量分量没有变化。
应用线性动量守恒和动能,并假设完全弹性碰撞,你会发现
在三维空间中,为了进行精确模拟,您必须在碰撞时计算平行且垂直于墙壁的动量分量,并对产生的速度变化进行编码。
换句话说,这段代码:
///////////////////reflection/////////////////
if(p0.r[j] >= 250)
{
p0.r[j] = 500 - p0.r[j];
p0.v[j] = -p0.v[j];
}
else if(p0.r[j] <= 0)
{
p0.r[j] = -p0.r[j];
p0.v[j] = -p0.v[j];
}
//////////////////////////////////////////////
没有正确地模拟反射的物理学。要解决这个问题,请参考以下内容:
例如,对于由X = 250,0 <= Y <250,0 <= Z <250定义的立方体的右壁,法向矢量处于负X方向。对于由X = 0,0 <= Y <250,0 <= Z <250定义的左壁,法向量处于正X方向。
因此,在这两面墙的反射中,速度的X分量会改变符号,因为它与墙壁是正常的(垂直),但Y和Z分量不会改变符号,因为它们与墙壁平行。
在立方体 - 左侧的顶部和底部墙(常数Y)以及前后墙(常数Z)上应用类似的考虑因素作为练习,以计算出这些表面的法线。
最后,你不应该改变反射位置矢量分量的符号,只改变速度矢量。而是在给定新速度的情况下重新计算位置向量的下一个值。
答案 1 :(得分:1)
F = ma
a = F / m
a dt = F / m dt
dt是固定时间内的加速度 - 该帧的速度变化。 您将其设置为F / m i 正如评论所暗示的那样 i 这是错误的。它需要是帧的持续时间,而不是到目前为止整个模拟的持续时间。
答案 2 :(得分:0)
好的,所以有一些问题。其他人指出需要使用Dt
而非i
进行整合步骤。
但是,你说正确的反思和节能问题是正确的。我在下面添加了明确的跟踪记录。
请注意,除了能量问题之外,反射的分量计算实际上很好。
问题是在反射过程中由于重力引起的加速度变化。在粒子撞击地板的情况下,它获得的动能等于它保持下降时的动能,但新位置具有更高的势能。因此,能量将增加地板与新位置之间潜在能量差的两倍。从屋顶反弹会产生相反的效果。
如下所述,一旦策略是计算实际反射时间。然而,实际上直接使用能量更简单,更稳健。但请注意,虽然下面的简单能量版本确保速度和位置一致,但它实际上没有正确的位置。对于大多数可能并不重要的目的。如果你真的需要正确的位置,我认为我们需要解决弹跳时间。
/* Start of the code */
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
struct pointmass
{
double m; // mass
double r[3]; // coordinates
double v[3]; // velocity
};
// Grav.constant
const double G[3] = { 0, -9.81, 0 };
int main()
{
// I've just changed the initial values to speed up unit testing; your code worked fine here.
int Time = 50; // Duration
double Dt = 1; // Time steps
pointmass p0;
p0.v[0] = 23;
p0.v[1] = 40;
p0.v[2] = 15;
p0.r[0] = 100;
p0.r[1] = 200;
p0.r[2] = 67;
for (double i = 0; i<Time; i += Dt)
{
cout << setw(10) << i << setw(10);
double energy = 0;
for (int j = 0; j <= 2; j++)
{
double oldR = p0.r[j];
double oldV = p0.v[j];
////////////position and velocity///////////
p0.r[j] = p0.r[j] + p0.v[j] * Dt + 0.5*G[j] * Dt*Dt;
p0.v[j] = p0.v[j] + G[j] * Dt;
///////////////////reflection/////////////////
if (G[j] == 0)
{
if (p0.r[j] >= 250)
{
p0.r[j] = 500 - p0.r[j];
p0.v[j] = -p0.v[j];
}
else if (p0.r[j] <= 0)
{
p0.r[j] = -p0.r[j];
p0.v[j] = -p0.v[j];
}
}
else
{
// Need to capture the fact that the acceleration switches direction relative to velocity half way through the timestep.
// Two approaches, either
// Try to compute the time of the bounce and work out the detail.
// OR
// Use conservation of energy to get the right speed - much easier!
if (p0.r[j] >= 250)
{
double energy = 0.5*p0.v[j] * p0.v[j] - G[j] * p0.r[j];
p0.r[j] = 500 - p0.r[j];
p0.v[j] = -sqrt(2 * (energy + G[j] * p0.r[j]));
}
else if (p0.r[j] <= 0)
{
double energy = 0.5*p0.v[j] * p0.v[j] - G[j] * p0.r[j];
p0.r[j] = -p0.r[j];
p0.v[j] = sqrt(2*(energy + G[j] * p0.r[j]));
}
}
energy += 0.5*p0.v[j] * p0.v[j] - G[j] * p0.r[j];
}
/////////////////////Output//////////////////
cout << energy << setw(10);
for (int j = 0; j <= 2; j++)
{
cout << p0.r[j] << setw(10);
}
for (int j = 0; j <= 2; j++)
{
cout << p0.v[j] << setw(10);
}
///////////////////////////////////////////////
cout << endl;
}
}