TNG version 1.7.3
[gromacs/AngularHB.git] / src / external / tng_io / src / tests / md_openmp.c
blobd500167e152d644bad8b4e3088bcc3ab7dceb288
1 #ifdef TNG_BUILD_OPENMP_EXAMPLES
3 #include "tng/tng_io.h"
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <time.h>
7 #include <math.h>
8 #include <omp.h>
10 int main ();
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 /******************************************************************************/
23 int main ()
25 /******************************************************************************/
27 Purpose:
29 MAIN is the main program for MD_OPENMP.
31 Discussion:
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.
44 Licensing:
46 This code is distributed under the GNU LGPL license.
48 Modified:
50 8 Jan 2013
52 Author:
54 Original FORTRAN77 version by Bill Magro.
55 C version by John Burkardt.
56 TNG trajectory output by Magnus Lundborg.
58 Parameters:
60 None
63 double *acc;
64 double *box;
65 double *box_shape;
66 double dt = 0.0002;
67 double e0;
68 double *force;
69 int i;
70 double kinetic;
71 double mass = 1.0;
72 int nd = 3;
73 int np = 50;
74 double *pos;
75 double potential;
76 int proc_num;
77 int seed = 123456789;
78 int step;
79 int step_num = 50000;
80 int step_print;
81 int step_print_index;
82 int step_print_num;
83 int step_save;
84 int64_t sparse_save;
85 double *vel;
86 double wtime;
87 tng_trajectory_t traj;
88 tng_molecule_t molecule;
89 tng_chain_t chain;
90 tng_residue_t residue;
91 tng_atom_t atom;
92 int64_t n_frames_per_frame_set;
93 int frames_saved_cnt = 0;
95 timestamp ( );
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 ) );
106 printf ( "\n" );
107 printf ( "MD_OPENMP\n" );
108 printf ( " C/OpenMP version\n" );
109 printf ( "\n" );
110 printf ( " A molecular dynamics program.\n" );
112 printf ( "\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 );
117 printf ( "\n" );
118 printf ( " Number of processors available = %d\n", proc_num );
119 printf ( " Number of threads = %d\n", omp_get_max_threads ( ) );
122 printf("\n");
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");
128 exit(1);
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");
143 exit(1);
145 tng_molecule_cnt_set(traj, molecule, np);
149 Set the dimensions of the box.
151 for(i = 0; i < 9; i++)
153 box_shape[i] = 0.0;
155 for ( i = 0; i < nd; i++ )
157 box[i] = 10.0;
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)
168 free(box_shape);
169 tng_trajectory_destroy(&traj);
170 printf(" Cannot write trajectory headers and box shape.\n");
171 exit(1);
173 free(box_shape);
175 printf ( "\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.
184 printf ( "\n" );
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 */
192 step_save = 500;
194 step_print = 0;
195 step_print_index = 0;
196 step_print_num = 10;
197 sparse_save = 100;
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",
205 step_save);
206 printf(" saved to a TNG trajectory file.\n");
207 printf ( "\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" );
212 printf ( "\n" );
213 printf ( " Step Potential Kinetic (P+K-E0)/E0\n" );
214 printf ( " Energy P Energy K Relative Energy Error\n" );
215 printf ( "\n" );
217 step = 0;
218 printf ( " %8d %14f %14f %14e\n",
219 step, potential, kinetic, ( potential + kinetic - e0 ) / e0 );
220 step_print_index++;
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__);
230 exit(1);
233 /* Add empty data blocks */
234 if(tng_particle_data_block_add(traj, TNG_TRAJ_POSITIONS,
235 "POSITIONS",
236 TNG_DOUBLE_DATA,
237 TNG_TRAJECTORY_BLOCK,
238 n_frames_per_frame_set, 3,
239 1, 0, np,
240 TNG_UNCOMPRESSED,
241 0) != TNG_SUCCESS)
243 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
244 exit(1);
246 if(tng_particle_data_block_add(traj, TNG_TRAJ_VELOCITIES,
247 "VELOCITIES",
248 TNG_DOUBLE_DATA,
249 TNG_TRAJECTORY_BLOCK,
250 n_frames_per_frame_set, 3,
251 1, 0, np,
252 TNG_UNCOMPRESSED,
253 0) != TNG_SUCCESS)
255 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
256 exit(1);
258 if(tng_particle_data_block_add(traj, TNG_TRAJ_FORCES,
259 "FORCES",
260 TNG_DOUBLE_DATA,
261 TNG_TRAJECTORY_BLOCK,
262 n_frames_per_frame_set, 3,
263 1, 0, np,
264 TNG_UNCOMPRESSED, 0) != TNG_SUCCESS)
266 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
267 exit(1);
270 /* There is no standard ID for potential energy. Pick one. The
271 potential energy will not be saved every frame - it is sparsely
272 saved. */
273 if(tng_data_block_add(traj, 10101,
274 "POTENTIAL ENERGY",
275 TNG_DOUBLE_DATA,
276 TNG_TRAJECTORY_BLOCK,
277 n_frames_per_frame_set, 1,
278 sparse_save, TNG_UNCOMPRESSED,
279 0) != TNG_SUCCESS)
281 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
282 exit(1);
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__);
289 exit(1);
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__);
299 exit(1);
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__);
306 exit(1);
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__);
313 exit(1);
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__);
321 exit(1);
324 frames_saved_cnt++;
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 );
334 step_print_index++;
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__);
344 exit(1);
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__);
351 exit(1);
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__);
358 exit(1);
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__);
366 exit(1);
369 frames_saved_cnt++;
371 update ( np, nd, pos, vel, force, acc, mass, dt );
373 wtime = omp_get_wtime ( ) - wtime;
375 printf ( "\n" );
376 printf ( " Elapsed time for main computation:\n" );
377 printf ( " %f seconds.\n", wtime );
379 free ( acc );
380 free ( box );
381 free ( force );
382 free ( pos );
383 free ( vel );
385 Terminate.
387 tng_trajectory_destroy(&traj);
389 printf ( "\n" );
390 printf ( "MD_OPENMP\n" );
391 printf ( " Normal end of execution.\n" );
393 printf ( "\n" );
394 timestamp ( );
396 return 0;
398 /******************************************************************************/
400 void compute ( int np, int nd, double pos[], double vel[],
401 double mass, double f[], double *pot, double *kin )
403 /******************************************************************************/
405 Purpose:
407 COMPUTE computes the forces and energies.
409 Discussion:
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 ) )
423 Licensing:
425 This code is distributed under the GNU LGPL license.
427 Modified:
429 21 November 2007
431 Author:
433 Original FORTRAN77 version by Bill Magro.
434 C version by John Burkardt.
436 Parameters:
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.
455 double d;
456 double d2;
457 int i;
458 int j;
459 int k;
460 double ke;
461 double pe;
462 double PI2 = 3.141592653589793 / 2.0;
463 double rij[3];
465 pe = 0.0;
466 ke = 0.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++ )
481 f[i+k*nd] = 0.0;
484 for ( j = 0; j < np; j++ )
486 if ( k != j )
488 d = dist ( nd, pos+k*nd, pos+j*nd, rij );
490 Attribute half of the potential energy to particle J.
492 if ( d < PI2 )
494 d2 = d;
496 else
498 d2 = PI2;
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;
520 *pot = pe;
521 *kin = ke;
523 return;
525 /******************************************************************************/
527 double dist ( int nd, double r1[], double r2[], double dr[] )
529 /******************************************************************************/
531 Purpose:
533 DIST computes the displacement (and its norm) between two particles.
535 Licensing:
537 This code is distributed under the GNU LGPL license.
539 Modified:
541 21 November 2007
543 Author:
545 Original FORTRAN77 version by Bill Magro.
546 C version by John Burkardt.
548 Parameters:
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.
559 double d;
560 int i;
562 d = 0.0;
563 for ( i = 0; i < nd; i++ )
565 dr[i] = r1[i] - r2[i];
566 d = d + dr[i] * dr[i];
568 d = sqrt ( d );
570 return d;
572 /******************************************************************************/
574 void initialize ( int np, int nd, double box[], int *seed, double pos[],
575 double vel[], double acc[] )
577 /******************************************************************************/
579 Purpose:
581 INITIALIZE initializes the positions, velocities, and accelerations.
583 Licensing:
585 This code is distributed under the GNU LGPL license.
587 Modified:
589 21 November 2007
591 Author:
593 Original FORTRAN77 version by Bill Magro.
594 C version by John Burkardt.
596 Parameters:
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.
614 int i;
615 int j;
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++ )
631 vel[i+j*nd] = 0.0;
634 for ( j = 0; j < np; j++ )
636 for ( i = 0; i < nd; i++ )
638 acc[i+j*nd] = 0.0;
641 return;
643 /******************************************************************************/
645 double r8_uniform_01 ( int *seed )
647 /******************************************************************************/
649 Purpose:
651 R8_UNIFORM_01 is a unit pseudorandom R8.
653 Discussion:
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.
663 Licensing:
665 This code is distributed under the GNU LGPL license.
667 Modified:
669 11 August 2004
671 Author:
673 John Burkardt
675 Reference:
677 Paul Bratley, Bennett Fox, Linus Schrage,
678 A Guide to Simulation,
679 Springer Verlag, pages 201-202, 1983.
681 Bennett Fox,
682 Algorithm 647:
683 Implementation and Relative Efficiency of Quasirandom
684 Sequence Generators,
685 ACM Transactions on Mathematical Software,
686 Volume 12, Number 4, pages 362-376, 1986.
688 Parameters:
690 Input/output, int *SEED, a seed for the random number generator.
692 Output, double R8_UNIFORM_01, a new pseudorandom variate, strictly between
693 0 and 1.
696 int k;
697 double r;
699 k = *seed / 127773;
701 *seed = 16807 * ( *seed - k * 127773 ) - k * 2836;
703 if ( *seed < 0 )
705 *seed = *seed + 2147483647;
708 r = ( double ) ( *seed ) * 4.656612875E-10;
710 return r;
712 /******************************************************************************/
714 void timestamp ( void )
716 /******************************************************************************/
718 Purpose:
720 TIMESTAMP prints the current YMDHMS date as a time stamp.
722 Example:
724 31 May 2001 09:45:54 AM
726 Licensing:
728 This code is distributed under the GNU LGPL license.
730 Modified:
732 24 September 2003
734 Author:
736 John Burkardt
738 Parameters:
740 None
743 # define TIME_SIZE 40
745 static char time_buffer[TIME_SIZE];
746 const struct tm *tm;
747 time_t now;
749 now = time ( NULL );
750 tm = localtime ( &now );
752 strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm );
754 printf ( "%s\n", time_buffer );
756 return;
757 # undef TIME_SIZE
759 /******************************************************************************/
761 void update ( int np, int nd, double pos[], double vel[], double f[],
762 double acc[], double mass, double dt )
764 /******************************************************************************/
766 Purpose:
768 UPDATE updates positions, velocities and accelerations.
770 Discussion:
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
778 a(t+dt) = f(t) / m
780 Licensing:
782 This code is distributed under the GNU LGPL license.
784 Modified:
786 17 April 2009
788 Author:
790 Original FORTRAN77 version by Bill Magro.
791 C version by John Burkardt.
793 Parameters:
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.
812 int i;
813 int j;
814 double rmass;
816 rmass = 1.0 / mass;
818 # pragma omp parallel \
819 shared ( acc, dt, f, nd, np, pos, rmass, vel ) \
820 private ( i, j )
822 # pragma omp for
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;
833 return;
836 #endif