1 #ifdef TNG_BUILD_OPENMP_EXAMPLES
8 #include "tng/tng_io.h"
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 tng_output_file_set(traj, TNG_EXAMPLE_FILES_DIR "tng_md_out.tng");
134 /* Set molecules data */
135 printf(" Creating molecules in trajectory.\n");
136 tng_molecule_add(traj, "water", &molecule);
137 tng_molecule_chain_add(traj, molecule, "W", &chain);
138 tng_chain_residue_add(traj, chain, "WAT", &residue);
139 if(tng_residue_atom_add(traj, residue, "O", "O", &atom) == TNG_CRITICAL)
141 tng_trajectory_destroy(&traj);
142 printf(" Cannot create molecules.\n");
145 tng_molecule_cnt_set(traj, molecule, np);
149 Set the dimensions of the box.
151 for(i = 0; i < 9; i++)
155 for ( i = 0; i < nd; i++ )
158 box_shape[i*nd + i] = box[i];
162 /* Add the box shape data block and write the file headers */
163 if(tng_data_block_add(traj, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE", TNG_DOUBLE_DATA,
164 TNG_NON_TRAJECTORY_BLOCK, 1, 9, 1, TNG_UNCOMPRESSED,
165 box_shape) == TNG_CRITICAL ||
166 tng_file_headers_write(traj, TNG_USE_HASH) == TNG_CRITICAL)
169 tng_trajectory_destroy(&traj);
170 printf(" Cannot write trajectory headers and box shape.\n");
176 printf ( " Initializing positions, velocities, and accelerations.\n" );
178 Set initial positions, velocities, and accelerations.
180 initialize ( np, nd, box, &seed, pos, vel, acc );
182 Compute the forces and energies.
185 printf ( " Computing initial forces and energies.\n" );
187 compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
189 e0 = potential + kinetic;
191 /* Saving frequency */
195 step_print_index = 0;
200 This is the main time stepping loop:
201 Compute forces and energies,
202 Update positions, velocities, accelerations.
204 printf(" Every %d steps particle positions, velocities and forces are\n",
206 printf(" saved to a TNG trajectory file.\n");
208 printf ( " At certain step intervals, we report the potential and kinetic energies.\n" );
209 printf ( " The sum of these energies should be a constant.\n" );
210 printf ( " As an accuracy check, we also print the relative error\n" );
211 printf ( " in the total energy.\n" );
213 printf ( " Step Potential Kinetic (P+K-E0)/E0\n" );
214 printf ( " Energy P Energy K Relative Energy Error\n" );
218 printf ( " %8d %14f %14f %14e\n",
219 step, potential, kinetic, ( potential + kinetic - e0 ) / e0 );
221 step_print = ( step_print_index * step_num ) / step_print_num;
223 /* Create a frame set for writing data */
224 tng_num_frames_per_frame_set_get(traj, &n_frames_per_frame_set);
225 if(tng_frame_set_new(traj, 0,
226 n_frames_per_frame_set) != TNG_SUCCESS)
228 printf("Error creating frame set %d. %s: %d\n",
229 i, __FILE__, __LINE__);
233 /* Add empty data blocks */
234 if(tng_particle_data_block_add(traj, TNG_TRAJ_POSITIONS,
237 TNG_TRAJECTORY_BLOCK,
238 n_frames_per_frame_set, 3,
243 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
246 if(tng_particle_data_block_add(traj, TNG_TRAJ_VELOCITIES,
249 TNG_TRAJECTORY_BLOCK,
250 n_frames_per_frame_set, 3,
255 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
258 if(tng_particle_data_block_add(traj, TNG_TRAJ_FORCES,
261 TNG_TRAJECTORY_BLOCK,
262 n_frames_per_frame_set, 3,
264 TNG_UNCOMPRESSED, 0) != TNG_SUCCESS)
266 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
270 /* There is no standard ID for potential energy. Pick one. The
271 potential energy will not be saved every frame - it is sparsely
273 if(tng_data_block_add(traj, 10101,
276 TNG_TRAJECTORY_BLOCK,
277 n_frames_per_frame_set, 1,
278 sparse_save, TNG_UNCOMPRESSED,
281 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
285 /* Write the frame set to disk */
286 if(tng_frame_set_write(traj, TNG_USE_HASH) != TNG_SUCCESS)
288 printf("Error writing frame set. %s: %d\n", __FILE__, __LINE__);
292 wtime = omp_get_wtime ( );
294 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
295 TNG_TRAJ_POSITIONS, 0, np,
296 pos, TNG_USE_HASH) != TNG_SUCCESS)
298 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
301 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
302 TNG_TRAJ_VELOCITIES, 0, np,
303 vel, TNG_USE_HASH) != TNG_SUCCESS)
305 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
308 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
309 TNG_TRAJ_FORCES, 0, np,
310 force, TNG_USE_HASH) != TNG_SUCCESS)
312 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
315 if(step % (step_save * sparse_save) == 0)
317 if(tng_frame_data_write(traj, frames_saved_cnt, 10101, &potential,
318 TNG_USE_HASH) != TNG_SUCCESS)
320 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
326 for ( step = 1; step < step_num; step++ )
328 compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
330 if ( step == step_print )
332 printf ( " %8d %14f %14f %14e\n", step, potential, kinetic,
333 ( potential + kinetic - e0 ) / e0 );
335 step_print = ( step_print_index * step_num ) / step_print_num;
337 if(step % step_save == 0)
339 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
340 TNG_TRAJ_POSITIONS, 0, np,
341 pos, TNG_USE_HASH) != TNG_SUCCESS)
343 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
346 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
347 TNG_TRAJ_VELOCITIES, 0, np,
348 vel, TNG_USE_HASH) != TNG_SUCCESS)
350 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
353 if(tng_frame_particle_data_write(traj, frames_saved_cnt,
354 TNG_TRAJ_FORCES, 0, np,
355 force, TNG_USE_HASH) != TNG_SUCCESS)
357 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
360 if(step % (step_save * sparse_save) == 0)
362 if(tng_frame_data_write(traj, frames_saved_cnt, 10101, &potential,
363 TNG_USE_HASH) != TNG_SUCCESS)
365 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
371 update ( np, nd, pos, vel, force, acc, mass, dt );
373 wtime = omp_get_wtime ( ) - wtime;
376 printf ( " Elapsed time for main computation:\n" );
377 printf ( " %f seconds.\n", wtime );
387 tng_trajectory_destroy(&traj);
390 printf ( "MD_OPENMP\n" );
391 printf ( " Normal end of execution.\n" );
398 /******************************************************************************/
400 void compute ( int np, int nd, double pos[], double vel[],
401 double mass, double f[], double *pot, double *kin )
403 /******************************************************************************/
407 COMPUTE computes the forces and energies.
411 The computation of forces and energies is fully parallel.
413 The potential function V(X) is a harmonic well which smoothly
414 saturates to a maximum value at PI/2:
416 v(x) = ( sin ( min ( x, PI2 ) ) )**2
418 The derivative of the potential is:
420 dv(x) = 2.0 * sin ( min ( x, PI2 ) ) * cos ( min ( x, PI2 ) )
421 = sin ( 2.0 * min ( x, PI2 ) )
425 This code is distributed under the GNU LGPL license.
433 Original FORTRAN77 version by Bill Magro.
434 C version by John Burkardt.
438 Input, int NP, the number of particles.
440 Input, int ND, the number of spatial dimensions.
442 Input, double POS[ND*NP], the position of each particle.
444 Input, double VEL[ND*NP], the velocity of each particle.
446 Input, double MASS, the mass of each particle.
448 Output, double F[ND*NP], the forces.
450 Output, double *POT, the total potential energy.
452 Output, double *KIN, the total kinetic energy.
462 double PI2 = 3.141592653589793 / 2.0;
468 # pragma omp parallel \
469 shared ( f, nd, np, pos, vel ) \
470 private ( i, j, k, rij, d, d2 )
473 # pragma omp for reduction ( + : pe, ke )
474 for ( k = 0; k < np; k++ )
477 Compute the potential energy and forces.
479 for ( i = 0; i < nd; i++ )
484 for ( j = 0; j < np; j++ )
488 d = dist ( nd, pos+k*nd, pos+j*nd, rij );
490 Attribute half of the potential energy to particle J.
501 pe = pe + 0.5 * pow ( sin ( d2 ), 2 );
503 for ( i = 0; i < nd; i++ )
505 f[i+k*nd] = f[i+k*nd] - rij[i] * sin ( 2.0 * d2 ) / d;
510 Compute the kinetic energy.
512 for ( i = 0; i < nd; i++ )
514 ke = ke + vel[i+k*nd] * vel[i+k*nd];
518 ke = ke * 0.5 * mass;
525 /******************************************************************************/
527 double dist ( int nd, double r1[], double r2[], double dr[] )
529 /******************************************************************************/
533 DIST computes the displacement (and its norm) between two particles.
537 This code is distributed under the GNU LGPL license.
545 Original FORTRAN77 version by Bill Magro.
546 C version by John Burkardt.
550 Input, int ND, the number of spatial dimensions.
552 Input, double R1[ND], R2[ND], the positions of the particles.
554 Output, double DR[ND], the displacement vector.
556 Output, double D, the Euclidean norm of the displacement.
563 for ( i = 0; i < nd; i++ )
565 dr[i] = r1[i] - r2[i];
566 d = d + dr[i] * dr[i];
572 /******************************************************************************/
574 void initialize ( int np, int nd, double box[], int *seed, double pos[],
575 double vel[], double acc[] )
577 /******************************************************************************/
581 INITIALIZE initializes the positions, velocities, and accelerations.
585 This code is distributed under the GNU LGPL license.
593 Original FORTRAN77 version by Bill Magro.
594 C version by John Burkardt.
598 Input, int NP, the number of particles.
600 Input, int ND, the number of spatial dimensions.
602 Input, double BOX[ND], specifies the maximum position
603 of particles in each dimension.
605 Input, int *SEED, a seed for the random number generator.
607 Output, double POS[ND*NP], the position of each particle.
609 Output, double VEL[ND*NP], the velocity of each particle.
611 Output, double ACC[ND*NP], the acceleration of each particle.
617 Give the particles random positions within the box.
619 for ( i = 0; i < nd; i++ )
621 for ( j = 0; j < np; j++ )
623 pos[i+j*nd] = box[i] * r8_uniform_01 ( seed );
627 for ( j = 0; j < np; j++ )
629 for ( i = 0; i < nd; i++ )
634 for ( j = 0; j < np; j++ )
636 for ( i = 0; i < nd; i++ )
643 /******************************************************************************/
645 double r8_uniform_01 ( int *seed )
647 /******************************************************************************/
651 R8_UNIFORM_01 is a unit pseudorandom R8.
655 This routine implements the recursion
657 seed = 16807 * seed mod ( 2**31 - 1 )
658 unif = seed / ( 2**31 - 1 )
660 The integer arithmetic never requires more than 32 bits,
661 including a sign bit.
665 This code is distributed under the GNU LGPL license.
677 Paul Bratley, Bennett Fox, Linus Schrage,
678 A Guide to Simulation,
679 Springer Verlag, pages 201-202, 1983.
683 Implementation and Relative Efficiency of Quasirandom
685 ACM Transactions on Mathematical Software,
686 Volume 12, Number 4, pages 362-376, 1986.
690 Input/output, int *SEED, a seed for the random number generator.
692 Output, double R8_UNIFORM_01, a new pseudorandom variate, strictly between
701 *seed = 16807 * ( *seed - k * 127773 ) - k * 2836;
705 *seed = *seed + 2147483647;
708 r = ( double ) ( *seed ) * 4.656612875E-10;
712 /******************************************************************************/
714 void timestamp ( void )
716 /******************************************************************************/
720 TIMESTAMP prints the current YMDHMS date as a time stamp.
724 31 May 2001 09:45:54 AM
728 This code is distributed under the GNU LGPL license.
743 # define TIME_SIZE 40
745 static char time_buffer[TIME_SIZE];
750 tm = localtime ( &now );
752 strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm );
754 printf ( "%s\n", time_buffer );
759 /******************************************************************************/
761 void update ( int np, int nd, double pos[], double vel[], double f[],
762 double acc[], double mass, double dt )
764 /******************************************************************************/
768 UPDATE updates positions, velocities and accelerations.
772 The time integration is fully parallel.
774 A velocity Verlet algorithm is used for the updating.
776 x(t+dt) = x(t) + v(t) * dt + 0.5 * a(t) * dt * dt
777 v(t+dt) = v(t) + 0.5 * ( a(t) + a(t+dt) ) * dt
782 This code is distributed under the GNU LGPL license.
790 Original FORTRAN77 version by Bill Magro.
791 C version by John Burkardt.
795 Input, int NP, the number of particles.
797 Input, int ND, the number of spatial dimensions.
799 Input/output, double POS[ND*NP], the position of each particle.
801 Input/output, double VEL[ND*NP], the velocity of each particle.
803 Input, double F[ND*NP], the force on each particle.
805 Input/output, double ACC[ND*NP], the acceleration of each particle.
807 Input, double MASS, the mass of each particle.
809 Input, double DT, the time step.
818 # pragma omp parallel \
819 shared ( acc, dt, f, nd, np, pos, rmass, vel ) \
823 for ( j = 0; j < np; j++ )
825 for ( i = 0; i < nd; i++ )
827 pos[i+j*nd] = pos[i+j*nd] + vel[i+j*nd] * dt + 0.5 * acc[i+j*nd] * dt * dt;
828 vel[i+j*nd] = vel[i+j*nd] + 0.5 * dt * ( f[i+j*nd] * rmass + acc[i+j*nd] );
829 acc[i+j*nd] = f[i+j*nd] * rmass;