c - 在分子动力学模拟中计算势能
问题描述
背景
想象一下,我们在一个长度为 L 的盒子里有 N 个粒子,它们相互作用(通过 Lennard Jones 势)。
我想计算系统的总势能。我实现了函数 POT,它计算所有粒子的所有贡献并给出正确的结果(这是经过测试的,可以假设为真)。
我还写了一个函数 POT_ONE,它只计算一个粒子相对于所有其他粒子的势能。这意味着如果我想计算总势能,我将不得不调用这个函数 N 次(确保粒子不与自身相互作用)然后除以 2,因为我重复计算了相互作用。
目标
我的目标是使第二个函数产生与第一个函数相同的结果。
问题
发生了一些非常奇怪的事情:如果我放 4 个粒子,这两个函数给出相同的结果。如果我放第五个,那就有偏差。然后对于 6,7,8 个粒子,它再次给出正确的结果,然后对于 N=9,我得到不同的结果。在 N=1000 的情况下,我从 POT_ONE 得到的结果类似于 113383820348202024。
我的 N=5 的结果是:-0.003911 使用 POT 和 12.864234 使用 POT_ONE
如果有人尝试运行代码并想要检查 N=4 的情况,他/她应该更改定义为全局变量的粒子数 (np),然后注释该行pos[12]=1;pos[13]=1;pos[14]=1;
。
代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*GENERAL PARAMETERS*/
int dim=3; //number of dimensions
int np=5; //number of particles
double L=36.413; //box length (A)
double invL=1/36.413; //inverse of box length
/*ARGON CHARACTERISTICS*/
double sig=3.4; // Angstroms (A)
double e=0.001; // eV
double distSQ(double array[]){
/*calculates the squared distance given the array x=[dx,dy,dz]*/
int i;
double r2=0;
for(i=0;i<dim;i++) r2+=array[i]*array[i];
return r2;
}//distSQ
void MIC(double dr[],double L, int dim){
/* MINIMUM IMAGE CONVENTION: dr[] is the array dr = [dx,dy,dz] describing relative
positions of two particles, L is the box length, dim the number
of dimensions */
int i;
for(i=0;i<dim;i++) dr[i]-=round(dr[i]*invL)*L;
}//MIC
void POT(double x1[],double* potential){
/*given the positions of each particle in the form x=[x0,y0,z0;x1,y1,z1;...;xn-1,yn-1,zn-1],
the number of dimensions dim and particles np, it calculates the potential energy of the configuration*/
//variables for potential calculation
int i,j,k;
double *x2;
double r2inv; // 1/r^2
double foo,bar;
double dr[dim];
*potential=0; // set potential energy to zero
//main part of POT
for(i=0;i<np-1;i++){
x2=x1+dim;
for(j=i+1;j<np;j++){
//calculate relative distances between particles i & j
//apply periodic BCs and then calculate squared distance
//and the potential energy between them.
for(k=0;k<dim;k++) dr[k] = x2[k]-x1[k];
MIC(dr,L,dim); //periodic boundary conditions
r2inv=1/distSQ(dr);
//calculate potential energy
foo = sig*sig*r2inv;
bar = foo*foo*foo;
*potential+=bar*(bar-1);
}//for j
x1+=dim;
}//for i
*potential*=4*e; //scale and give energy units
}//POT
void POT_ONE(int particle,double pos[],double* potential){
*potential=0;
int i,k;
double dr[dim];
double r2inv,foo,bar;
double par_pos[dim];
int index=particle*dim;
par_pos[0]=pos[index];
par_pos[1]=pos[index+1];
par_pos[2]=pos[index+2];
for(i=0;i<np;i++){
if(i!=particle){
for(k=0;k<dim;k++) dr[k]=pos[k]-par_pos[k];
MIC(dr,L,dim);
r2inv=1/distSQ(dr);
foo=sig*sig*r2inv;
bar=foo*foo*foo;
*potential+=bar*(bar-1);
}
pos+=dim;
}
*potential*=4*e; //scale and give energy units
}//POT_ONE
int main(){
int D=np*dim;
double* pos=malloc(D*sizeof(double));
double potential=0; //calculated with POT
double U=0; ////calculated with POT_ONE
double tempU=0;
pos[0]=0;pos[1]=0;pos[2]=0;
pos[3]=4;pos[4]=0;pos[5]=0;
pos[6]=0;pos[7]=4;pos[8]=0;
pos[9]=0;pos[10]=0;pos[11]=4;
pos[12]=1;pos[13]=1;pos[14]=1;
POT(pos,&potential);
printf("POT: %f\n", potential);
int i,j;
for(i=0;i<np;i++){
POT_ONE(i,pos,&tempU);
U+=tempU;
}
U=U/2;
printf("POT_ONE: %f\n\n", U);
return 0;
}
解决方案
您的错误在POT
,您忘记x2
在内部循环结束时进行更新。
for (i = 0; i < np - 1; i++) {
double *x2 = x1 + dim;
for (j = i + 1; j < np; j++) {
// ... calculate stuff ..
x2 += dim;
}
x1 += dim;
}
一个更容易且可以说更具可读性的变体是完全放弃指针运算并使用无聊的旧索引:
for (k = 0; k < dim; k++) {
dr[k] = x[j * dim + k] - x[i * dim + k];
}
进一步的观察:
- 请使您的变量在使用它们的范围内成为本地变量。函数顶部的大量未初始化变量使得跟踪变量变得非常困难,即使在像你这样的短函数中也是如此。
- 请考虑从函数返回单个值,而不是传入指针。在我看来,这使得像距离平方这样的函数更具可读性。
- 你的代码结构很难看出来,因为一切都非常紧密地运行在一起,甚至是注释。
推荐阅读
- javascript - 从 web_accessible_resources 中列出的 javascript 文件访问 chrome 存储 API
- python - Python - var = var = var 值
- html - 悬停时显示表格行边框
- php - 在 Woocommerce 购物车页面中自动更新购物车数量变化
- php - 我想通过单个 onchange 事件调用多个跨度
- c# - 将 Cognito 与 C# 无服务器应用程序一起使用
- javascript - 如何在javascript中将ajax数据设置为数据表
- java - webivew中的Android支付网关错误
- django - UnicodeEncodeError:'charmap'编解码器无法编码字符 - Python Report CSS
- powershell - PowerShell - 使用未签名证书查询 REST API