实验内容

N体问题是指找出已知初始位置、速度和质量的多个物体在经典力学情况下的后续运动。在本次实验中,你需要模拟N个物体在二维空间中的运动情况。通过计算每两个物体之间的相互作用力,可以确定下一个时间周期内的物体位置。

在本次实验中,N个小球在均匀分布在一个正方形的二维空间中,小球在运动时没有范围限制。每个小球间会且只会受到其他小球的引力作用。在计算作用力时,两个小球间的距离不会低于其半径之和,在其他的地方小球位置的移动不会受到其他小球的影响(即不会发生碰撞,挡住等情况)。你需要计算模拟一定时间后小球的分布情况,并通过MPI并行化计算过程。

实验要求

有关参数要求如下:

  1. 引力常数数值取6.67*10^-11
  2. 小球重量都为 10000kg
  3. 小球半径都为1cm
  4. 小球间的初始间隔为1cm,例:N=36时,则初始的正方形区域为5cm*5cm
  5. 小球初速为0.

对于时间间隔,公式:delta_t=1/timestep。其中,timestep表示在1s内程序迭代的次数,小球每隔delta_t时间更新作用力,速度,位置信息。结果中程序总的迭代次数=timestep*模拟过程经历的时间,你可以根据你的硬件环境自己设置这些数值,理论上来说,时间间隔越小,模拟的真实度越高。

算法设计与分析

不考虑小球间的碰撞;小球可重叠,但是在计算距离时最小距离为半径。

首先初始化小球的位置速度加速度
循环:
compute_force()更新加速度。加速度需要依赖于所有小球的位置

compute_velocitiescompute_positions更新位置和速度。这两项只依赖于自身的属性

并行的实现:
将小球按进程数连续地均分,每个进程计算自己的部分;在计算加速度之前要接受其他进程的小球数据,并向其他进程发送自己的小球数据

核心代码

  1. //小球结构体:
  2. typedef struct ball{
  3. double px,py;
  4. double vx,vy;
  5. double ax,ay;
  6. }ball;
  7. ball ball_list[256];

计算加速度:每次计算之前都初始化加速度为0,然后遍历计算每一个其他小球。首先计算小球间距离,如果小于半径就以半径计算

Index表示要计算的小球,ball_list是全局变量

  1. void compute_force(int index){
  2. ball_list[index].ax=0;
  3. ball_list[index].ay=0;
  4. for(int i=0;i<N;i++){
  5. if(i!=index){
  6. double dx=ball_list[i].px-ball_list[index].px;
  7. double dy=ball_list[i].py-ball_list[index].py;
  8. double d=(dx*dx+dy*dy);
  9. if(d<r*r)d=r*r;
  10. d*=sqrt(d);//^(3/2)
  11. ball_list[index].ax+=GM*(dx)/d;
  12. ball_list[index].ay+=GM*(dy)/d;
  13. }
  14. }
  15. }

计算速度:

  1. void compute_velocities(int index){
  2. ball_list[index].vx+=ball_list[index].ax*delta_t;
  3. ball_list[index].vy+=ball_list[index].ay*delta_t;
  4. }

计算位置:注意小球不能超越边界,但是不考虑碰撞

  1. void compute_positions(int index){
  2. ball_list[index].px+=ball_list[index].vx*delta_t;
  3. if(ball_list[index].px>((size-1)/100.0))ball_list[index].px=(size-1)/100.0;
  4. if(ball_list[index].px<0)ball_list[index].px=0;
  5. ball_list[index].py+=ball_list[index].vy*delta_t;
  6. if(ball_list[index].py>((size-1)/100.0))ball_list[index].py=(size-1)/100.0;
  7. if(ball_list[index].py<0)ball_list[index].py=0;
  8. }

核心循环:

  1. //向所有其他线程发送本线程所拥有的数据
  2. for(int j=0;j<numprocs;j++){
  3. if(j!=myid)MPI_Bsend((ball_list+(N/numprocs)*myid),sizeof(ball)*N/numprocs,MPI_BYTE,j,i*10+myid,MPI_COMM_WORLD);
  4. }
  5. //从所有其他线程接收其他线程拥有的数据
  6. for(int j=0;j<numprocs;j++){
  7. if(j!=myid){
  8. MPI_Status status;
  9. MPI_Recv((ball_list+(N/numprocs)*j),sizeof(ball)*N/numprocs,MPI_BYTE,j,i*10+j,MPI_COMM_WORLD,&status);
  10. }
  11. }
  12. //首先计算加速度
  13. for(int j=(N/numprocs)*myid;j<(N/numprocs)*(myid+1);j++)
  14. compute_force(j);
  15. //之后更新位置会影响加速度的计算,所以要等所有线程计算完之后在计算
  16. MPI_Barrier(MPI_COMM_WORLD);
  17. //更新速度和位置
  18. for(int j=(N/numprocs)*myid;j<(N/numprocs)*(myid+1);j++){
  19. compute_velocities(j);
  20. compute_positions(j);
  21. }
  22. //位置的更新会影响下一个循环的加速度的计算,所以要同步
  23. MPI_Barrier(MPI_COMM_WORLD);

实验结果

规模 1 2 4
N=64/时间/s 1.636738          1.097428            0.956398            
加速比 1 1.491430873 1.711357
N=256/时间/s 22.832278 13.928187 11.09423
加速比 1 1.639285716 2.058031

小球的位置:

初始:

2000周期,Timestep=10000

为什么在四个角落里会有小球:在调试中发现,timestep设置的过小会因为数据精度不足导致位置无法更新,将timestep设置为100就不会出现这种情况

2000周期,Timestep=100

20000周期,timestep=100

分析与总结

MPI是基于进程通信的并行计算,所以进程间不会共享数据,要使用其他进程的数据的时候需要进行通信

源程序:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #include <math.h>
  5. #include <mpi.h>
  6. typedef struct ball
  7. {
  8. double px,py;
  9. double vx,vy;
  10. double ax,ay;
  11. }ball;
  12. ball ball_list[256];
  13. const int N=256;
  14. const double GM=6.67E-7;//G*M
  15. int timestep=100;
  16. double delta_t=0.0;
  17. double r=0.01;//球的半径
  18. int cycle_times=20000;//周期数
  19. int size=0;//方阵宽
  20. FILE* fp;
  21. char* filename[4]={"result1.txt","result2.txt","result3.txt","result4.txt"};
  22. void compute_force(int index)
  23. {
  24. ball_list[index].ax=0;
  25. ball_list[index].ay=0;
  26. for(int i=0;i<N;i++)
  27. {
  28. if(i!=index)
  29. {
  30. double dx=ball_list[i].px-ball_list[index].px;
  31. double dy=ball_list[i].py-ball_list[index].py;
  32. double d=(dx*dx+dy*dy);
  33. if(d<r*r)d=r*r;
  34. d*=sqrt(d);//^(3/2)
  35. ball_list[index].ax+=GM*(dx)/d;
  36. ball_list[index].ay+=GM*(dy)/d;
  37. //printf("%lf %lf ",dx,dy);
  38. }
  39. }
  40. //printf("%d a: %lf %lf\n",index,ball_list[index].ax,ball_list[index].ay);
  41. }
  42. void compute_velocities(int index)
  43. {
  44. ball_list[index].vx+=ball_list[index].ax*delta_t;
  45. ball_list[index].vy+=ball_list[index].ay*delta_t;
  46. //printf("%d v: %lf %lf\n",index,ball_list[index].vx,ball_list[index].vy);
  47. }
  48. void compute_positions(int index)
  49. {
  50. ball_list[index].px+=ball_list[index].vx*delta_t;
  51. if(ball_list[index].px>((size-1)/100.0))ball_list[index].px=(size-1)/100.0;
  52. if(ball_list[index].px<0)ball_list[index].px=0;
  53. ball_list[index].py+=ball_list[index].vy*delta_t;
  54. if(ball_list[index].py>((size-1)/100.0))ball_list[index].py=(size-1)/100.0;
  55. if(ball_list[index].py<0)ball_list[index].py=0;
  56. //printf("%d p: %lf %lf\n",index,ball_list[index].px,ball_list[index].py);
  57. }
  58. void print()
  59. {
  60. //int table[16][16]={0};
  61. for(int i=0;i<N;i++)
  62. {
  63. //table[(int)(ball_list[i].px*100)][(int)(ball_list[i].py*100)]++;
  64. fprintf(fp,"%lf\n",ball_list[i].px);
  65. }
  66. fprintf(fp,"\n");
  67. for(int i=0;i<N;i++)
  68. {
  69. //table[(int)(ball_list[i].px*100)][(int)(ball_list[i].py*100)]++;
  70. fprintf(fp,"%lf\n",ball_list[i].py);
  71. }
  72. fprintf(fp, "end of printing\n\n");
  73. }
  74. void main(int argc,char *argv[])
  75. {
  76. //init
  77. delta_t=1.0/timestep;
  78. //printf("%lf\n",delta_t);
  79. size=(int)sqrt(N);
  80. //printf("%d\n",size);
  81. for(int i=0;i<N;i++)
  82. {
  83. ball_list[i].px=0.01*(i%size);
  84. ball_list[i].py=0.01*(i/size);
  85. ball_list[i].vx=0;
  86. ball_list[i].vy=0;
  87. ball_list[i].ax=0;
  88. ball_list[i].ay=0;
  89. }
  90. int myid, numprocs;
  91. clock_t starttime,endtime;
  92. int namelen;
  93. MPI_Init(&argc,&argv);
  94. MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
  95. MPI_Comm_rank(MPI_COMM_WORLD,&myid);
  96. double* mpi_buffer=malloc(sizeof(double)*1000000);
  97. MPI_Buffer_attach(mpi_buffer,sizeof(double)*1000000);
  98. //模拟开始
  99. //if(myid==0)print();
  100. starttime=clock();
  101. for(int i=0;i<cycle_times;i++)
  102. {
  103. for(int j=0;j<numprocs;j++)
  104. {
  105. if(j!=myid)
  106. MPI_Bsend((ball_list+(N/numprocs)*myid),sizeof(ball)*N/numprocs,MPI_BYTE,j,i*10+myid,MPI_COMM_WORLD);
  107. }
  108. for(int j=0;j<numprocs;j++)
  109. {
  110. if(j!=myid)
  111. {
  112. MPI_Status status;
  113. MPI_Recv((ball_list+(N/numprocs)*j),sizeof(ball)*N/numprocs,MPI_BYTE,j,i*10+j,MPI_COMM_WORLD,&status);
  114. }
  115. }
  116. for(int j=(N/numprocs)*myid;j<(N/numprocs)*(myid+1);j++)
  117. {
  118. compute_force(j);
  119. }
  120. MPI_Barrier(MPI_COMM_WORLD);
  121. for(int j=(N/numprocs)*myid;j<(N/numprocs)*(myid+1);j++)
  122. {
  123. compute_velocities(j);
  124. compute_positions(j);
  125. }
  126. //printf("\n");
  127. MPI_Barrier(MPI_COMM_WORLD);
  128. }
  129. endtime=clock();
  130. printf("rank=%d time:%lf\n",myid,(double)(endtime-starttime)/CLOCKS_PER_SEC);
  131. //printf("id %d\n",myid);
  132. if(myid!=0)
  133. {
  134. //printf("sid %d %d %d\n",myid,ball_list+(N/numprocs)*myid,sizeof(ball)*N/numprocs);
  135. MPI_Send((ball_list+(N/numprocs)*myid),sizeof(ball)*N/numprocs,MPI_BYTE,0,myid,MPI_COMM_WORLD);
  136. }
  137. if(myid==0)
  138. {
  139. fp = fopen(filename[numprocs-1],"w");
  140. for(int i=1;i<numprocs;i++)
  141. {
  142. MPI_Status status;
  143. //printf("rid %d %d %d\n",myid,ball_list+(N/numprocs)*myid,sizeof(ball)*N/numprocs);
  144. MPI_Recv((ball_list+(N/numprocs)*i),sizeof(ball)*N/numprocs,MPI_BYTE,i,i,MPI_COMM_WORLD,&status);
  145. }
  146. print();
  147. fclose(fp);
  148. }
  149. MPI_Finalize();
  150. }

MPI并行计算模拟N体问题的更多相关文章

  1. 矩阵乘法的MPI并行计算

    1.问题描述 矩阵乘法问题描述如下: 给定矩阵A和B,其中A是m*p大小矩阵,B是p*n大小的矩阵.求C = A*B. 求解这个问题最简单的算法是遍历A的行和B的列,求得C的相应元素,时间复杂度O(m ...

  2. 【并行计算】用MPI进行分布式内存编程(一)

    通过上一篇关于并行计算准备部分的介绍,我们知道MPI(Message-Passing-Interface 消息传递接口)实现并行是进程级别的,通过通信在进程之间进行消息传递.MPI并不是一种新的开发语 ...

  3. 浅说CPU并行计算与GPU并行计算

    最近在学一门课,叫做“C++与并行计算”.要用到多CPU(进程)并行的原理,实现语言是C++的MPI接口.联想到上学期用到CUDA C/C++来做并行计算,就对这两门语言做一个总结,分享下自己关于并行 ...

  4. 关于mpi的理论知识以及编写程序来实现数据积分中的梯形积分法。

    几乎所有人的第一个程序是从“hello,world”程序开始学习的 #include "mpi.h" #include <stdio.h> int main(int a ...

  5. VMware workstation运维实践系列博客导航

    第一章:VMware workstation虚拟化1.1 VMware workstation计算网络存储介绍1.2 VMware workstation其他功能特性介绍1.3 VMware work ...

  6. [.net 多线程] Interlocked实现CAS操作

    Interlocked:为多个线程共享的变量提供原子操作. Interlocked.Increment(ref value) 数值加一(原子性操作) Interlocked.Decrement(ref ...

  7. 高性能集群(HPC

    串行计算与并行计算1.串行计算串行计算是指在单个计算机(拥有单个中央独立单元) 上执行软件写操作.CPU 逐个使用一系列指令解决问题.为了加快处理速度,在原有的串行计算的基础上演变出并行计算2.并行计 ...

  8. 基于MPI的并行计算—矩阵向量乘

    以前没接触过MPI编程,对并行计算也没什么了解.朋友的期末课程作业让我帮忙写一写,哎,实现结果很一般啊.最终也没完整完成任务,惭愧惭愧. 问题大概是利用MPI完成矩阵和向量相乘.输入:Am×n,Bn× ...

  9. MPI n 体问题

    ▶ <并行程序设计导论>第六章中讨论了 n 体问题,分别使用了 MPI,Pthreads,OpenMP 来进行实现,这里是 MPI 的代码,分为基本算法和简化算法(引力计算量为基本算法的一 ...

随机推荐

  1. 如何显示bootstrap fileinput缩略图上面的删除按钮

    bootstrap上传文件控件初始化js: //bootstrap上传文件控件 $(".fileupload").fileinput({ language: "zh&qu ...

  2. Appserv 2.5.10 升级PHP from version 5.2 to 5.3

    解决方案查看 该文章:http://blog.csdn.net/dull_boy2/article/details/43927363

  3. STM8S103之串口

    1.串口发送中断标志的清除,只能靠往UART_DR中写数据,这个的本质含义是,发送中断是指发送完成中断,所以往UART_DR中写数据可以清除发送中断标志.但是这样又会导致新写的数据完成后又会产生中断, ...

  4. 设置fixed,横向滚动条失效

    window.onscroll = function(){ var sl = -Math.max(document.body.scrollLeft,document.documentElement.s ...

  5. iOS开发——AFNetworking基于https的使用

    应公司项目需求,之前的项目使用的http,新项目要求使用https,这篇博客是在AFNetworking框架基于http的基础上修改而来. 1.在开始前,先要把 .crt 文件转成 .cer 文件,然 ...

  6. POJ 1742 Coins(多重背包?)

    题解 一个自然的思路是对于每一个物品做一次01背包 然后T飞了. 试着用二进制拆分,还是T了. 单调队列,对不起,懒,不想写. 我们这样想.设dp[i]代表i这个面值前几种硬币是否能凑到 然后对于每一 ...

  7. 【mysql】 mysql 子查询、联合查询、模糊查询、排序、聚合函数、分组----------语法

    第二章 mysql 一.模糊查询 like 1. 字段 like '河北省%' %代表任何N个字符 2 字段 like '河北省____' _代表任意1个字符 二.IN 语法:SELECT 字段列1, ...

  8. 【Computer Vision】图像单应性变换/投影/仿射/透视

    一.基础概念 1. projective transformation  = homography = collineation. 2. 齐次坐标:使用N+1维坐标来表示N维坐标,例如在2D笛卡尔坐标 ...

  9. Python: Json串反序列化为自定义类对象

    最近刚接触到python,就想到了如何反序列化json串.网上找了一下,大部分都是用json模块反序列化为python数据结构(字典和列表).如果对json模块不了解的参考菜鸟教程.然后我在此基础上将 ...

  10. P1017 进制转换 (负进制转换)

    和平常的转化差不多 加多一步 如果余数 < 0, 那么余数减去除数(此时除数是负),商数加1 #include<cstdio> #define _for(i, a, b) for(i ...