1 #ifdef TNG_BUILD_OPENMP_EXAMPLES
11 void compute ( int np, int nd, double pos[], double vel[],
12 double mass, double f[], double *pot, double *kin );
13 double dist ( int nd, double r1[], double r2[], double dr[] );
14 void initialize ( int np, int nd, double box[], int *seed, double pos[],
15 double vel[], double acc[] );
16 double r8_uniform_01 ( int *seed );
17 void timestamp ( void );
18 void update ( int np, int nd, double pos[], double vel[], double f[],
19 double acc[], double mass, double 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.
46 This code is distributed under the GNU LGPL license.
54 Original FORTRAN77 version by Bill Magro.
55 C version by John Burkardt.
56 TNG trajectory output by Magnus Lundborg.
87 tng_trajectory_t traj;
88 tng_molecule_t molecule;
90 tng_residue_t residue;
92 int64_t n_frames_per_frame_set;
93 int frames_saved_cnt = 0;
97 proc_num = omp_get_num_procs ( );
99 acc = ( double * ) malloc ( nd * np * sizeof ( double ) );
100 box = ( double * ) malloc ( nd * sizeof ( double ) );
101 box_shape = (double *) malloc (9 * sizeof (double));
102 force = ( double * ) malloc ( nd * np * sizeof ( double ) );
103 pos = ( double * ) malloc ( nd * np * sizeof ( double ) );
104 vel = ( double * ) malloc ( nd * np * sizeof ( double ) );
107 printf ( "MD_OPENMP\n" );
108 printf ( " C/OpenMP version\n" );
110 printf ( " A molecular dynamics program.\n" );
113 printf ( " NP, the number of particles in the simulation is %d\n", np );
114 printf ( " STEP_NUM, the number of time steps, is %d\n", step_num );
115 printf ( " DT, the size of each time step, is %f\n", dt );
118 printf ( " Number of processors available = %d\n", proc_num );
119 printf ( " Number of threads = %d\n", omp_get_max_threads ( ) );
123 printf(" Initializing trajectory storage.\n");
124 if(tng_trajectory_init(&traj) != TNG_SUCCESS)
126 tng_trajectory_destroy(&traj);
127 printf(" Cannot init trajectory.\n");
130 #ifdef TNG_EXAMPLE_FILES_DIR
131 tng_output_file_set(traj, TNG_EXAMPLE_FILES_DIR "tng_md_out.tng");
133 tng_output_file_set(traj, "/tmp/tng_md_out.tng");
138 /* Set molecules data */
139 printf(" Creating molecules in trajectory.\n");
140 tng_molecule_add(traj, "water", &molecule);
141 tng_molecule_chain_add(traj, molecule, "W", &chain);
142 tng_chain_residue_add(traj, chain, "WAT", &residue);
143 if(tng_residue_atom_add(traj, residue, "O", "O", &atom) == TNG_CRITICAL)
145 tng_trajectory_destroy(&traj);
146 printf(" Cannot create molecules.\n");
149 tng_molecule_cnt_set(traj, molecule, np);
153 Set the dimensions of the box.
155 for(i = 0; i < 9; i++)
159 for ( i = 0; i < nd; i++ )
162 box_shape[i*nd + i] = box[i];
166 /* Add the box shape data block and write the file headers */
167 if(tng_data_block_add(traj, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE", TNG_DOUBLE_DATA,
168 TNG_NON_TRAJECTORY_BLOCK, 1, 9, 1, TNG_UNCOMPRESSED,
169 box_shape) == TNG_CRITICAL ||
170 tng_file_headers_write(traj, TNG_USE_HASH) == TNG_CRITICAL)
173 tng_trajectory_destroy(&traj);
174 printf(" Cannot write trajectory headers and box shape.\n");
180 printf ( " Initializing positions, velocities, and accelerations.\n" );
182 Set initial positions, velocities, and accelerations.
184 initialize ( np, nd, box, &seed, pos, vel, acc );
186 Compute the forces and energies.
189 printf ( " Computing initial forces and energies.\n" );
191 compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
193 e0 = potential + kinetic;
195 /* Saving frequency */
199 step_print_index = 0;
204 This is the main time stepping loop:
205 Compute forces and energies,
206 Update positions, velocities, accelerations.
208 printf(" Every %d steps particle positions, velocities and forces are\n",
210 printf(" saved to a TNG trajectory file.\n");
212 printf ( " At certain step intervals, we report the potential and kinetic energies.\n" );
213 printf ( " The sum of these energies should be a constant.\n" );
214 printf ( " As an accuracy check, we also print the relative error\n" );
215 printf ( " in the total energy.\n" );
217 printf ( " Step Potential Kinetic (P+K-E0)/E0\n" );
218 printf ( " Energy P Energy K Relative Energy Error\n" );
222 printf ( " %8d %14f %14f %14e\n",
223 step, potential, kinetic, ( potential + kinetic - e0 ) / e0 );
225 step_print = ( step_print_index * step_num ) / step_print_num;
227 /* Create a frame set for writing data */
228 tng_num_frames_per_frame_set_get(traj, &n_frames_per_frame_set);
229 if(tng_frame_set_new(traj, 0,
230 n_frames_per_frame_set) != TNG_SUCCESS)
232 printf("Error creating frame set %d. %s: %d\n",
233 i, __FILE__, __LINE__);
237 /* Add empty data blocks */
238 if(tng_particle_data_block_add(traj, TNG_TRAJ_POSITIONS,
241 TNG_TRAJECTORY_BLOCK,
242 n_frames_per_frame_set, 3,
247 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
250 if(tng_particle_data_block_add(traj, TNG_TRAJ_VELOCITIES,
253 TNG_TRAJECTORY_BLOCK,
254 n_frames_per_frame_set, 3,
259 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
262 if(tng_particle_data_block_add(traj, TNG_TRAJ_FORCES,
265 TNG_TRAJECTORY_BLOCK,
266 n_frames_per_frame_set, 3,
268 TNG_UNCOMPRESSED, 0) != TNG_SUCCESS)
270 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
274 /* There is no standard ID for potential energy. Pick one. The
275 potential energy will not be saved every frame - it is sparsely
277 if(tng_data_block_add(traj, 10101,
280 TNG_TRAJECTORY_BLOCK,
281 n_frames_per_frame_set, 1,
282 sparse_save, TNG_UNCOMPRESSED,
285 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
289 /* Write the frame set to disk */
290 if(tng_frame_set_write(traj, TNG_USE_HASH) != TNG_SUCCESS)
292 printf("Error writing frame set. %s: %d\n", __FILE__, __LINE__);
296 wtime = omp_get_wtime ( );
298 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
299 TNG_TRAJ_POSITIONS, 0, np,
300 pos, TNG_USE_HASH) != TNG_SUCCESS)
302 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
305 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
306 TNG_TRAJ_VELOCITIES, 0, np,
307 vel, TNG_USE_HASH) != TNG_SUCCESS)
309 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
312 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
313 TNG_TRAJ_FORCES, 0, np,
314 force, TNG_USE_HASH) != TNG_SUCCESS)
316 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
319 if(step % (step_save * sparse_save) == 0)
321 if(tng_frame_data_write(traj, frames_saved_cnt, 10101, &potential,
322 TNG_USE_HASH) != TNG_SUCCESS)
324 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
330 for ( step = 1; step < step_num; step++ )
332 compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
334 if ( step == step_print )
336 printf ( " %8d %14f %14f %14e\n", step, potential, kinetic,
337 ( potential + kinetic - e0 ) / e0 );
339 step_print = ( step_print_index * step_num ) / step_print_num;
341 if(step % step_save == 0)
343 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
344 TNG_TRAJ_POSITIONS, 0, np,
345 pos, TNG_USE_HASH) != TNG_SUCCESS)
347 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
350 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
351 TNG_TRAJ_VELOCITIES, 0, np,
352 vel, TNG_USE_HASH) != TNG_SUCCESS)
354 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
357 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
358 TNG_TRAJ_FORCES, 0, np,
359 force, TNG_USE_HASH) != TNG_SUCCESS)
361 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
364 if(step % (step_save * sparse_save) == 0)
366 if(tng_frame_data_write(traj, frames_saved_cnt, 10101, &potential,
367 TNG_USE_HASH) != TNG_SUCCESS)
369 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
375 update ( np, nd, pos, vel, force, acc, mass, dt );
377 wtime = omp_get_wtime ( ) - wtime;
380 printf ( " Elapsed time for main computation:\n" );
381 printf ( " %f seconds.\n", wtime );
391 tng_trajectory_destroy(&traj);
394 printf ( "MD_OPENMP\n" );
395 printf ( " Normal end of execution.\n" );
402 /******************************************************************************/
404 void compute ( int np, int nd, double pos[], double vel[],
405 double mass, double f[], double *pot, double *kin )
407 /******************************************************************************/
411 COMPUTE computes the forces and energies.
415 The computation of forces and energies is fully parallel.
417 The potential function V(X) is a harmonic well which smoothly
418 saturates to a maximum value at PI/2:
420 v(x) = ( sin ( min ( x, PI2 ) ) )**2
422 The derivative of the potential is:
424 dv(x) = 2.0 * sin ( min ( x, PI2 ) ) * cos ( min ( x, PI2 ) )
425 = sin ( 2.0 * min ( x, PI2 ) )
429 This code is distributed under the GNU LGPL license.
437 Original FORTRAN77 version by Bill Magro.
438 C version by John Burkardt.
442 Input, int NP, the number of particles.
444 Input, int ND, the number of spatial dimensions.
446 Input, double POS[ND*NP], the position of each particle.
448 Input, double VEL[ND*NP], the velocity of each particle.
450 Input, double MASS, the mass of each particle.
452 Output, double F[ND*NP], the forces.
454 Output, double *POT, the total potential energy.
456 Output, double *KIN, the total kinetic energy.
466 double PI2 = 3.141592653589793 / 2.0;
472 # pragma omp parallel \
473 shared ( f, nd, np, pos, vel ) \
474 private ( i, j, k, rij, d, d2 )
477 # pragma omp for reduction ( + : pe, ke )
478 for ( k = 0; k < np; k++ )
481 Compute the potential energy and forces.
483 for ( i = 0; i < nd; i++ )
488 for ( j = 0; j < np; j++ )
492 d = dist ( nd, pos+k*nd, pos+j*nd, rij );
494 Attribute half of the potential energy to particle J.
505 pe = pe + 0.5 * pow ( sin ( d2 ), 2 );
507 for ( i = 0; i < nd; i++ )
509 f[i+k*nd] = f[i+k*nd] - rij[i] * sin ( 2.0 * d2 ) / d;
514 Compute the kinetic energy.
516 for ( i = 0; i < nd; i++ )
518 ke = ke + vel[i+k*nd] * vel[i+k*nd];
522 ke = ke * 0.5 * mass;
529 /******************************************************************************/
531 double dist ( int nd, double r1[], double r2[], double dr[] )
533 /******************************************************************************/
537 DIST computes the displacement (and its norm) between two particles.
541 This code is distributed under the GNU LGPL license.
549 Original FORTRAN77 version by Bill Magro.
550 C version by John Burkardt.
554 Input, int ND, the number of spatial dimensions.
556 Input, double R1[ND], R2[ND], the positions of the particles.
558 Output, double DR[ND], the displacement vector.
560 Output, double D, the Euclidean norm of the displacement.
567 for ( i = 0; i < nd; i++ )
569 dr[i] = r1[i] - r2[i];
570 d = d + dr[i] * dr[i];
576 /******************************************************************************/
578 void initialize ( int np, int nd, double box[], int *seed, double pos[],
579 double vel[], double acc[] )
581 /******************************************************************************/
585 INITIALIZE initializes the positions, velocities, and accelerations.
589 This code is distributed under the GNU LGPL license.
597 Original FORTRAN77 version by Bill Magro.
598 C version by John Burkardt.
602 Input, int NP, the number of particles.
604 Input, int ND, the number of spatial dimensions.
606 Input, double BOX[ND], specifies the maximum position
607 of particles in each dimension.
609 Input, int *SEED, a seed for the random number generator.
611 Output, double POS[ND*NP], the position of each particle.
613 Output, double VEL[ND*NP], the velocity of each particle.
615 Output, double ACC[ND*NP], the acceleration of each particle.
621 Give the particles random positions within the box.
623 for ( i = 0; i < nd; i++ )
625 for ( j = 0; j < np; j++ )
627 pos[i+j*nd] = box[i] * r8_uniform_01 ( seed );
631 for ( j = 0; j < np; j++ )
633 for ( i = 0; i < nd; i++ )
638 for ( j = 0; j < np; j++ )
640 for ( i = 0; i < nd; i++ )
647 /******************************************************************************/
649 double r8_uniform_01 ( int *seed )
651 /******************************************************************************/
655 R8_UNIFORM_01 is a unit pseudorandom R8.
659 This routine implements the recursion
661 seed = 16807 * seed mod ( 2**31 - 1 )
662 unif = seed / ( 2**31 - 1 )
664 The integer arithmetic never requires more than 32 bits,
665 including a sign bit.
669 This code is distributed under the GNU LGPL license.
681 Paul Bratley, Bennett Fox, Linus Schrage,
682 A Guide to Simulation,
683 Springer Verlag, pages 201-202, 1983.
687 Implementation and Relative Efficiency of Quasirandom
689 ACM Transactions on Mathematical Software,
690 Volume 12, Number 4, pages 362-376, 1986.
694 Input/output, int *SEED, a seed for the random number generator.
696 Output, double R8_UNIFORM_01, a new pseudorandom variate, strictly between
705 *seed = 16807 * ( *seed - k * 127773 ) - k * 2836;
709 *seed = *seed + 2147483647;
712 r = ( double ) ( *seed ) * 4.656612875E-10;
716 /******************************************************************************/
718 void timestamp ( void )
720 /******************************************************************************/
724 TIMESTAMP prints the current YMDHMS date as a time stamp.
728 31 May 2001 09:45:54 AM
732 This code is distributed under the GNU LGPL license.
747 # define TIME_SIZE 40
749 static char time_buffer[TIME_SIZE];
754 tm = localtime ( &now );
756 strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm );
758 printf ( "%s\n", time_buffer );
763 /******************************************************************************/
765 void update ( int np, int nd, double pos[], double vel[], double f[],
766 double acc[], double mass, double dt )
768 /******************************************************************************/
772 UPDATE updates positions, velocities and accelerations.
776 The time integration is fully parallel.
778 A velocity Verlet algorithm is used for the updating.
780 x(t+dt) = x(t) + v(t) * dt + 0.5 * a(t) * dt * dt
781 v(t+dt) = v(t) + 0.5 * ( a(t) + a(t+dt) ) * dt
786 This code is distributed under the GNU LGPL license.
794 Original FORTRAN77 version by Bill Magro.
795 C version by John Burkardt.
799 Input, int NP, the number of particles.
801 Input, int ND, the number of spatial dimensions.
803 Input/output, double POS[ND*NP], the position of each particle.
805 Input/output, double VEL[ND*NP], the velocity of each particle.
807 Input, double F[ND*NP], the force on each particle.
809 Input/output, double ACC[ND*NP], the acceleration of each particle.
811 Input, double MASS, the mass of each particle.
813 Input, double DT, the time step.
822 # pragma omp parallel \
823 shared ( acc, dt, f, nd, np, pos, rmass, vel ) \
827 for ( j = 0; j < np; j++ )
829 for ( i = 0; i < nd; i++ )
831 pos[i+j*nd] = pos[i+j*nd] + vel[i+j*nd] * dt + 0.5 * acc[i+j*nd] * dt * dt;
832 vel[i+j*nd] = vel[i+j*nd] + 0.5 * dt * ( f[i+j*nd] * rmass + acc[i+j*nd] );
833 acc[i+j*nd] = f[i+j*nd] * rmass;