1 #ifdef TNG_BUILD_OPENMP_EXAMPLES
11 void compute ( int np, int nd, float pos[], float vel[],
12 float mass, float f[], float *pot, float *kin );
13 float dist ( int nd, float r1[], float r2[], float dr[] );
14 void initialize ( int np, int nd, float box[], int *seed, float pos[],
15 float vel[], float acc[] );
16 float r8_uniform_01 ( int *seed );
17 void timestamp ( void );
18 void update ( int np, int nd, float pos[], float vel[], float f[],
19 float acc[], float mass, float dt );
21 /******************************************************************************/
25 /******************************************************************************/
29 MAIN is the main program for MD_OPENMP.
33 MD implements a simple molecular dynamics simulation.
35 The program uses Open MP directives to allow parallel computation.
37 The velocity Verlet time integration scheme is used.
39 The particles interact with a central pair potential.
41 Output of the program is saved in the TNG format, which is why this
42 code is included in the TNG API release. The high-level API of the
43 TNG API is used where appropriate.
47 This code is distributed under the GNU LGPL license.
55 Original FORTRAN77 version by Bill Magro.
56 C version by John Burkardt.
57 TNG trajectory output by Magnus Lundborg.
87 tng_trajectory_t traj;
88 tng_molecule_t molecule;
90 tng_residue_t residue;
95 proc_num = omp_get_num_procs ( );
97 acc = ( float * ) malloc ( nd * np * sizeof ( float ) );
98 box = ( float * ) malloc ( nd * sizeof ( float ) );
99 box_shape = (float *) malloc (9 * sizeof (float));
100 force = ( float * ) malloc ( nd * np * sizeof ( float ) );
101 pos = ( float * ) malloc ( nd * np * sizeof ( float ) );
102 vel = ( float * ) malloc ( nd * np * sizeof ( float ) );
105 printf ( "MD_OPENMP\n" );
106 printf ( " C/OpenMP version\n" );
108 printf ( " A molecular dynamics program.\n" );
111 printf ( " NP, the number of particles in the simulation is %d\n", np );
112 printf ( " STEP_NUM, the number of time steps, is %d\n", step_num );
113 printf ( " DT, the size of each time step, is %f\n", dt );
116 printf ( " Number of processors available = %d\n", proc_num );
117 printf ( " Number of threads = %d\n", omp_get_max_threads ( ) );
121 printf(" Initializing trajectory storage.\n");
122 /* Initialize the TNG trajectory */
123 #ifdef TNG_EXAMPLE_FILES_DIR
124 tng_util_trajectory_open(TNG_EXAMPLE_FILES_DIR "tng_md_out.tng", 'w', &traj);
126 tng_util_trajectory_open("/tmp/tng_md_out.tng", 'w', &traj);
131 /* Set molecules data */
132 /* N.B. This is still not done using utility functions. The low-level API
134 printf(" Creating molecules in trajectory.\n");
135 tng_molecule_add(traj, "water", &molecule);
136 tng_molecule_chain_add(traj, molecule, "W", &chain);
137 tng_chain_residue_add(traj, chain, "WAT", &residue);
138 if(tng_residue_atom_add(traj, residue, "O", "O", &atom) == TNG_CRITICAL)
140 tng_util_trajectory_close(&traj);
141 printf(" Cannot create molecules.\n");
144 tng_molecule_cnt_set(traj, molecule, np);
148 Set the dimensions of the box.
150 for(i = 0; i < 9; i++)
154 for ( i = 0; i < nd; i++ )
157 /* box_shape stores 9 values according to the TNG specs */
158 box_shape[i*nd + i] = box[i];
163 printf ( " Initializing positions, velocities, and accelerations.\n" );
165 Set initial positions, velocities, and accelerations.
167 initialize ( np, nd, box, &seed, pos, vel, acc );
169 Compute the forces and energies.
172 printf ( " Computing initial forces and energies.\n" );
174 compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
176 e0 = potential + kinetic;
178 /* Saving frequency */
182 step_print_index = 0;
186 This is the main time stepping loop:
187 Compute forces and energies,
188 Update positions, velocities, accelerations.
190 printf(" Every %d steps box shape, particle positions, velocities and forces are\n",
192 printf(" saved to a TNG trajectory file.\n");
194 printf ( " At certain step intervals, we report the potential and kinetic energies.\n" );
195 printf ( " The sum of these energies should be a constant.\n" );
196 printf ( " As an accuracy check, we also print the relative error\n" );
197 printf ( " in the total energy.\n" );
199 printf ( " Step Potential Kinetic (P+K-E0)/E0\n" );
200 printf ( " Energy P Energy K Relative Energy Error\n" );
204 printf ( " %8d %14f %14f %14e\n",
205 step, potential, kinetic, ( potential + kinetic - e0 ) / e0 );
207 step_print = ( step_print_index * step_num ) / step_print_num;
209 /* Set the output frequency of box shape, positions, velocities and forces */
210 if(tng_util_box_shape_write_frequency_set(traj, step_save) != TNG_SUCCESS)
212 printf("Error setting writing frequency data. %s: %d\n",
216 if(tng_util_pos_write_frequency_set(traj, step_save) != TNG_SUCCESS)
218 printf("Error setting writing frequency data. %s: %d\n",
222 if(tng_util_vel_write_frequency_set(traj, step_save) != TNG_SUCCESS)
224 printf("Error setting writing frequency data. %s: %d\n",
228 if(tng_util_force_write_frequency_set(traj, step_save) != TNG_SUCCESS)
230 printf("Error setting writing frequency data. %s: %d\n",
235 /* Write the first frame of box shape, positions, velocities and forces */
236 if(tng_util_box_shape_write(traj, 0, box_shape) != TNG_SUCCESS)
238 printf("Error writing box shape. %s: %d\n",
242 if(tng_util_pos_write(traj, 0, pos) != TNG_SUCCESS)
244 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
247 if(tng_util_vel_write(traj, 0, vel) != TNG_SUCCESS)
249 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
252 if(tng_util_force_write(traj, 0, force) != TNG_SUCCESS)
254 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
258 wtime = omp_get_wtime ( );
260 for ( step = 1; step < step_num; step++ )
262 compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
264 if ( step == step_print )
266 printf ( " %8d %14f %14f %14e\n", step, potential, kinetic,
267 ( potential + kinetic - e0 ) / e0 );
269 step_print = ( step_print_index * step_num ) / step_print_num;
271 if(step % step_save == 0)
273 /* Write box shape, positions, velocities and forces */
274 if(tng_util_box_shape_write(traj, step, box_shape) != TNG_SUCCESS)
276 printf("Error writing box shape. %s: %d\n",
280 if(tng_util_pos_write(traj, step, pos) != TNG_SUCCESS)
282 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
285 if(tng_util_vel_write(traj, step, vel) != TNG_SUCCESS)
287 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
290 if(tng_util_force_write(traj, step, force) != TNG_SUCCESS)
292 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
296 update ( np, nd, pos, vel, force, acc, mass, dt );
298 wtime = omp_get_wtime ( ) - wtime;
301 printf ( " Elapsed time for main computation:\n" );
302 printf ( " %f seconds.\n", wtime );
311 /* Close the TNG output. */
312 tng_util_trajectory_close(&traj);
315 printf ( "MD_OPENMP\n" );
316 printf ( " Normal end of execution.\n" );
323 /******************************************************************************/
325 void compute ( int np, int nd, float pos[], float vel[],
326 float mass, float f[], float *pot, float *kin )
328 /******************************************************************************/
332 COMPUTE computes the forces and energies.
336 The computation of forces and energies is fully parallel.
338 The potential function V(X) is a harmonic well which smoothly
339 saturates to a maximum value at PI/2:
341 v(x) = ( sin ( min ( x, PI2 ) ) )**2
343 The derivative of the potential is:
345 dv(x) = 2.0 * sin ( min ( x, PI2 ) ) * cos ( min ( x, PI2 ) )
346 = sin ( 2.0 * min ( x, PI2 ) )
350 This code is distributed under the GNU LGPL license.
358 Original FORTRAN77 version by Bill Magro.
359 C version by John Burkardt.
363 Input, int NP, the number of particles.
365 Input, int ND, the number of spatial dimensions.
367 Input, float POS[ND*NP], the position of each particle.
369 Input, float VEL[ND*NP], the velocity of each particle.
371 Input, float MASS, the mass of each particle.
373 Output, float F[ND*NP], the forces.
375 Output, float *POT, the total potential energy.
377 Output, float *KIN, the total kinetic energy.
387 float PI2 = 3.141592653589793 / 2.0;
393 # pragma omp parallel \
394 shared ( f, nd, np, pos, vel ) \
395 private ( i, j, k, rij, d, d2 )
398 # pragma omp for reduction ( + : pe, ke )
399 for ( k = 0; k < np; k++ )
402 Compute the potential energy and forces.
404 for ( i = 0; i < nd; i++ )
409 for ( j = 0; j < np; j++ )
413 d = dist ( nd, pos+k*nd, pos+j*nd, rij );
415 Attribute half of the potential energy to particle J.
426 pe = pe + 0.5 * pow ( sin ( d2 ), 2 );
428 for ( i = 0; i < nd; i++ )
430 f[i+k*nd] = f[i+k*nd] - rij[i] * sin ( 2.0 * d2 ) / d;
435 Compute the kinetic energy.
437 for ( i = 0; i < nd; i++ )
439 ke = ke + vel[i+k*nd] * vel[i+k*nd];
443 ke = ke * 0.5 * mass;
450 /******************************************************************************/
452 float dist ( int nd, float r1[], float r2[], float dr[] )
454 /******************************************************************************/
458 DIST computes the displacement (and its norm) between two particles.
462 This code is distributed under the GNU LGPL license.
470 Original FORTRAN77 version by Bill Magro.
471 C version by John Burkardt.
475 Input, int ND, the number of spatial dimensions.
477 Input, float R1[ND], R2[ND], the positions of the particles.
479 Output, float DR[ND], the displacement vector.
481 Output, float D, the Euclidean norm of the displacement.
488 for ( i = 0; i < nd; i++ )
490 dr[i] = r1[i] - r2[i];
491 d = d + dr[i] * dr[i];
497 /******************************************************************************/
499 void initialize ( int np, int nd, float box[], int *seed, float pos[],
500 float vel[], float acc[] )
502 /******************************************************************************/
506 INITIALIZE initializes the positions, velocities, and accelerations.
510 This code is distributed under the GNU LGPL license.
518 Original FORTRAN77 version by Bill Magro.
519 C version by John Burkardt.
523 Input, int NP, the number of particles.
525 Input, int ND, the number of spatial dimensions.
527 Input, float BOX[ND], specifies the maximum position
528 of particles in each dimension.
530 Input, int *SEED, a seed for the random number generator.
532 Output, float POS[ND*NP], the position of each particle.
534 Output, float VEL[ND*NP], the velocity of each particle.
536 Output, float ACC[ND*NP], the acceleration of each particle.
542 Give the particles random positions within the box.
544 for ( i = 0; i < nd; i++ )
546 for ( j = 0; j < np; j++ )
548 pos[i+j*nd] = box[i] * r8_uniform_01 ( seed );
552 for ( j = 0; j < np; j++ )
554 for ( i = 0; i < nd; i++ )
559 for ( j = 0; j < np; j++ )
561 for ( i = 0; i < nd; i++ )
568 /******************************************************************************/
570 float r8_uniform_01 ( int *seed )
572 /******************************************************************************/
576 R8_UNIFORM_01 is a unit pseudorandom R8.
580 This routine implements the recursion
582 seed = 16807 * seed mod ( 2**31 - 1 )
583 unif = seed / ( 2**31 - 1 )
585 The integer arithmetic never requires more than 32 bits,
586 including a sign bit.
590 This code is distributed under the GNU LGPL license.
602 Paul Bratley, Bennett Fox, Linus Schrage,
603 A Guide to Simulation,
604 Springer Verlag, pages 201-202, 1983.
608 Implementation and Relative Efficiency of Quasirandom
610 ACM Transactions on Mathematical Software,
611 Volume 12, Number 4, pages 362-376, 1986.
615 Input/output, int *SEED, a seed for the random number generator.
617 Output, float R8_UNIFORM_01, a new pseudorandom variate, strictly between
626 *seed = 16807 * ( *seed - k * 127773 ) - k * 2836;
630 *seed = *seed + 2147483647;
633 r = ( float ) ( *seed ) * 4.656612875E-10;
637 /******************************************************************************/
639 void timestamp ( void )
641 /******************************************************************************/
645 TIMESTAMP prints the current YMDHMS date as a time stamp.
649 31 May 2001 09:45:54 AM
653 This code is distributed under the GNU LGPL license.
668 # define TIME_SIZE 40
670 static char time_buffer[TIME_SIZE];
675 tm = localtime ( &now );
677 strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm );
679 printf ( "%s\n", time_buffer );
684 /******************************************************************************/
686 void update ( int np, int nd, float pos[], float vel[], float f[],
687 float acc[], float mass, float dt )
689 /******************************************************************************/
693 UPDATE updates positions, velocities and accelerations.
697 The time integration is fully parallel.
699 A velocity Verlet algorithm is used for the updating.
701 x(t+dt) = x(t) + v(t) * dt + 0.5 * a(t) * dt * dt
702 v(t+dt) = v(t) + 0.5 * ( a(t) + a(t+dt) ) * dt
707 This code is distributed under the GNU LGPL license.
715 Original FORTRAN77 version by Bill Magro.
716 C version by John Burkardt.
720 Input, int NP, the number of particles.
722 Input, int ND, the number of spatial dimensions.
724 Input/output, float POS[ND*NP], the position of each particle.
726 Input/output, float VEL[ND*NP], the velocity of each particle.
728 Input, float F[ND*NP], the force on each particle.
730 Input/output, float ACC[ND*NP], the acceleration of each particle.
732 Input, float MASS, the mass of each particle.
734 Input, float DT, the time step.
743 # pragma omp parallel \
744 shared ( acc, dt, f, nd, np, pos, rmass, vel ) \
748 for ( j = 0; j < np; j++ )
750 for ( i = 0; i < nd; i++ )
752 pos[i+j*nd] = pos[i+j*nd] + vel[i+j*nd] * dt + 0.5 * acc[i+j*nd] * dt * dt;
753 vel[i+j*nd] = vel[i+j*nd] + 0.5 * dt * ( f[i+j*nd] * rmass + acc[i+j*nd] );
754 acc[i+j*nd] = f[i+j*nd] * rmass;