运行 Java 多线程下的分子动力学模拟程序

Run Java Molecular Dynamic simulation program in multithreading

我这里有这个小程序,我必须让它在多线程中工作。我尝试这样做,但是当将线程数设置为 1 时,程序似乎按预期运行。所以,一般程序运行正常。但是如果我设置值让我们说 2 或 4。变得疯狂。我没有得到所需的结果。好像是计算出了问题。我用头撞墙,试图找出问题所在,但我尝试的一切似乎都无法解决问题。这是顺序代码:

/*
 * "Physics" part of code adapted from Dan Schroeder's applet at:
 *
 *     http://physics.weber.edu/schroeder/software/mdapplet.html
 */

import java.awt.* ;
import javax.swing.* ;

public class MD {

    // Size of simulation

    final static int N = 2000 ;   // Number of "atoms"
    final static double BOX_WIDTH = 100.0 ;


    // Initial state - controls temperature of system

    //final static double VELOCITY = 3.0 ;  // gaseous
    final static double VELOCITY = 2.0 ;  // gaseous/"liquid"
    //final static double VELOCITY = 1.0 ;  // "crystalline"

    final static double INIT_SEPARATION = 2.2 ;  // in atomic radii


    // Simulation

    final static double DT = 0.01 ;  // Time step


    // Display

    final static int WINDOW_SIZE = 800 ;
    final static int DELAY = 0 ;
    final static int OUTPUT_FREQ = 20 ;


    // Physics constants

    final static double ATOM_RADIUS = 0.5 ;

    final static double WALL_STIFFNESS = 500.0 ;
    final static double GRAVITY = 0.005 ;
    final static double FORCE_CUTOFF = 3.0 ;


    // Atom positions
    static double [] x = new double [N] ;
    static double [] y = new double [N] ;

    // Atom velocities
    static double [] vx = new double [N] ;
    static double [] vy = new double [N] ;

    // Atom accelerations
    static double [] ax = new double [N] ;
    static double [] ay = new double [N] ;


    public static void main(String args []) throws Exception {

        Display display = new Display() ;

        // Define initial state of atoms

        int sqrtN = (int) (Math.sqrt((double) N) + 0.5) ;
        double initSeparation = INIT_SEPARATION * ATOM_RADIUS ;
        for(int i = 0 ; i < N ; i++) {
            // lay out atoms regularly, so no overlap
            x [i] = (0.5 + i % sqrtN) * initSeparation ;
            y [i] = (0.5 + i / sqrtN) * initSeparation ;
            vx [i] = (2 * Math.random() - 1) * VELOCITY ;
            vy [i] = (2 * Math.random() - 1) * VELOCITY ;
        } 

        int iter = 0 ;
        while(true) {

            if(iter % OUTPUT_FREQ == 0) {
                System.out.println("iter = " + iter + ", time = " + iter * DT) ;
                display.repaint() ;
                Thread.sleep(DELAY) ;
            }

            // Verlet integration:
            // http://en.wikipedia.org/wiki/Verlet_integration#Velocity_Verlet

            double dtOver2 = 0.5 * DT;
            double dtSquaredOver2 = 0.5 * DT * DT;
            for (int i = 0; i < N; i++) {
                x[i] += (vx[i] * DT) + (ax[i] * dtSquaredOver2);
                        // update position
                y[i] += (vy[i] * DT) + (ay[i] * dtSquaredOver2);
                vx[i] += (ax[i] * dtOver2);  // update velocity halfway
                vy[i] += (ay[i] * dtOver2);
            }

            computeAccelerations();

            for (int i = 0; i < N; i++) {
                vx[i] += (ax[i] * dtOver2);
                        // finish updating velocity with new acceleration
                vy[i] += (ay[i] * dtOver2);
            }

            iter++ ;
        }
    }

    // Compute accelerations of all atoms from current positions:
    static void computeAccelerations() {

        double dx, dy;  // separations in x and y directions
        double dx2, dy2, rSquared, rSquaredInv, attract, repel, fOverR, fx, fy;

        // first check for bounces off walls, and include gravity (if any):
        for (int i = 0; i < N; i++) {
            if (x[i] < ATOM_RADIUS) {
                ax[i] = WALL_STIFFNESS * (ATOM_RADIUS - x[i]);
            }
            else if (x[i] > (BOX_WIDTH - ATOM_RADIUS)) {
                ax[i] = WALL_STIFFNESS * (BOX_WIDTH - ATOM_RADIUS - x[i]);
            }
            else {
                ax[i] = 0.0;
            }
            if (y[i] < ATOM_RADIUS) {
                ay[i] = (WALL_STIFFNESS * (ATOM_RADIUS - y[i]));
            }
            else if (y[i] > (BOX_WIDTH - ATOM_RADIUS)) {
                ay[i] = (WALL_STIFFNESS * (BOX_WIDTH - ATOM_RADIUS - y[i]));
            }
            else {
                ay[i] = 0;
            }
            ay[i] -= GRAVITY ;
        }

        double forceCutoff2 = FORCE_CUTOFF * FORCE_CUTOFF ;

        // Now compute interaction forces (Lennard-Jones potential).
        // This is where the program spends most of its time.


        for (int i = 1; i < N; i++) {
            for (int j = 0; j < i; j++) {           // loop over all distinct pairs
                dx = x[i] - x[j];
                dx2 = dx * dx;
                if (dx2 < forceCutoff2) {               // make sure they're close enough to bother
                    dy = y[i] - y[j];
                    dy2 = dy * dy;
                    if (dy2 < forceCutoff2) {
                        rSquared = dx2 + dy2;
                        if (rSquared < forceCutoff2) {
                            rSquaredInv = 1.0 / rSquared;
                            attract = rSquaredInv * rSquaredInv * rSquaredInv;
                            repel = attract * attract;
                            fOverR = 24.0 * ((2.0 * repel) - attract) * rSquaredInv;
                            fx = fOverR * dx;
                            fy = fOverR * dy;
                            ax[i] += fx;  // add this force on to i's acceleration (mass = 1)
                            ay[i] += fy;

                            ax[j] -= fx;  // Newton's 3rd law
                            ay[j] -= fy;
                        }
                    }
                }
            }
        }
    }


    static class Display extends JPanel {

        static final double SCALE = WINDOW_SIZE / BOX_WIDTH ;

        static final int DIAMETER =
                Math.max((int) (SCALE * 2 * ATOM_RADIUS), 2) ;

        Display() {

            setPreferredSize(new Dimension(WINDOW_SIZE, WINDOW_SIZE)) ;

            JFrame frame = new JFrame("MD");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setContentPane(this);
            frame.pack();
            frame.setVisible(true);
        }

        public void paintComponent(Graphics g) {
            g.setColor(Color.WHITE) ;
            g.fillRect(0, 0, WINDOW_SIZE, WINDOW_SIZE) ;
            g.setColor(Color.BLUE) ;
            for(int i = 0 ; i < N ; i++) {
                g.fillOval((int) (SCALE * (x [i] - ATOM_RADIUS)),
                           WINDOW_SIZE - 1 - (int) (SCALE * (y [i] + ATOM_RADIUS)),
                           DIAMETER, DIAMETER) ;
            } 
        }
    }
}

有人能找出问题所在吗?

beginend 被声明为静态的,因此所有线程在计算期间都错误地使用了相同的变量。

A) begin/end 将被错误地设置为线程之间在其方法 运行()

开始时竞争条件的随机输出

B) 所有线程都可能在相同的索引范围内工作,忽略位置、速度、加速度数组的其余部分。

另外所有线程都是运行宁不必要完全相同的代码:

           for (int i = 0; i < N; i++) {
                x[i] += (vx[i] * DT) + (ax[i] * dtSquaredOver2);
                // update position
            ...

我会建议将所有变量更改为实例变量,然后将代码修改为静态,仅那些应该在线程之间共享的变量。静态变量的读取没问题,如果它们是独占的,则必须检查写入。 (写入 x、vx 的不同部分...从这个意义上说,数组是 'exclusive',只需确保索引不重叠)。