Bumped TNG to latest version.
[gromacs.git] / src / external / tng_io / src / tests / md_openmp_util.c
blob34b8964144e5a5412a55783a88d80fbc3b1f024a
1 #ifdef TNG_BUILD_OPENMP_EXAMPLES
3 # include <stdlib.h>
4 # include <stdio.h>
5 # include <time.h>
6 # include <math.h>
7 # include <omp.h>
8 # include "tng_io.h"
10 int main ();
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 /******************************************************************************/
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. The high-level API of the
43 TNG API is used where appropriate.
45 Licensing:
47 This code is distributed under the GNU LGPL license.
49 Modified:
51 8 Jan 2013
53 Author:
55 Original FORTRAN77 version by Bill Magro.
56 C version by John Burkardt.
57 TNG trajectory output by Magnus Lundborg.
59 Parameters:
61 None
64 float *acc;
65 float *box;
66 float *box_shape;
67 float dt = 0.0002;
68 float e0;
69 float *force;
70 int i;
71 float kinetic;
72 float mass = 1.0;
73 int nd = 3;
74 int np = 50;
75 float *pos;
76 float potential;
77 int proc_num;
78 int seed = 123456789;
79 int step;
80 int step_num = 50000;
81 int step_print;
82 int step_print_index;
83 int step_print_num;
84 int step_save;
85 float *vel;
86 float 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;
93 timestamp ( );
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 ) );
104 printf ( "\n" );
105 printf ( "MD_OPENMP\n" );
106 printf ( " C/OpenMP version\n" );
107 printf ( "\n" );
108 printf ( " A molecular dynamics program.\n" );
110 printf ( "\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 );
115 printf ( "\n" );
116 printf ( " Number of processors available = %d\n", proc_num );
117 printf ( " Number of threads = %d\n", omp_get_max_threads ( ) );
120 printf("\n");
121 printf(" Initializing trajectory storage.\n");
122 /* Initialize the TNG trajectory */
123 tng_util_trajectory_open(TNG_EXAMPLE_FILES_DIR "tng_md_out.tng", 'w', &traj);
127 /* Set molecules data */
128 /* N.B. This is still not done using utility functions. The low-level API
129 * is used. */
130 printf(" Creating molecules in trajectory.\n");
131 tng_molecule_add(traj, "water", &molecule);
132 tng_molecule_chain_add(traj, molecule, "W", &chain);
133 tng_chain_residue_add(traj, chain, "WAT", &residue);
134 if(tng_residue_atom_add(traj, residue, "O", "O", &atom) == TNG_CRITICAL)
136 tng_util_trajectory_close(&traj);
137 printf(" Cannot create molecules.\n");
138 exit(1);
140 tng_molecule_cnt_set(traj, molecule, np);
144 Set the dimensions of the box.
146 for(i = 0; i < 9; i++)
148 box_shape[i] = 0.0;
150 for ( i = 0; i < nd; i++ )
152 box[i] = 10.0;
153 /* box_shape stores 9 values according to the TNG specs */
154 box_shape[i*nd + i] = box[i];
158 printf ( "\n" );
159 printf ( " Initializing positions, velocities, and accelerations.\n" );
161 Set initial positions, velocities, and accelerations.
163 initialize ( np, nd, box, &seed, pos, vel, acc );
165 Compute the forces and energies.
167 printf ( "\n" );
168 printf ( " Computing initial forces and energies.\n" );
170 compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
172 e0 = potential + kinetic;
174 /* Saving frequency */
175 step_save = 400;
177 step_print = 0;
178 step_print_index = 0;
179 step_print_num = 10;
182 This is the main time stepping loop:
183 Compute forces and energies,
184 Update positions, velocities, accelerations.
186 printf(" Every %d steps box shape, particle positions, velocities and forces are\n",
187 step_save);
188 printf(" saved to a TNG trajectory file.\n");
189 printf ( "\n" );
190 printf ( " At certain step intervals, we report the potential and kinetic energies.\n" );
191 printf ( " The sum of these energies should be a constant.\n" );
192 printf ( " As an accuracy check, we also print the relative error\n" );
193 printf ( " in the total energy.\n" );
194 printf ( "\n" );
195 printf ( " Step Potential Kinetic (P+K-E0)/E0\n" );
196 printf ( " Energy P Energy K Relative Energy Error\n" );
197 printf ( "\n" );
199 step = 0;
200 printf ( " %8d %14f %14f %14e\n",
201 step, potential, kinetic, ( potential + kinetic - e0 ) / e0 );
202 step_print_index++;
203 step_print = ( step_print_index * step_num ) / step_print_num;
205 /* Set the output frequency of box shape, positions, velocities and forces */
206 if(tng_util_box_shape_write_frequency_set(traj, step_save) != TNG_SUCCESS)
208 printf("Error setting writing frequency data. %s: %d\n",
209 __FILE__, __LINE__);
210 exit(1);
212 if(tng_util_pos_write_frequency_set(traj, step_save) != TNG_SUCCESS)
214 printf("Error setting writing frequency data. %s: %d\n",
215 __FILE__, __LINE__);
216 exit(1);
218 if(tng_util_vel_write_frequency_set(traj, step_save) != TNG_SUCCESS)
220 printf("Error setting writing frequency data. %s: %d\n",
221 __FILE__, __LINE__);
222 exit(1);
224 if(tng_util_force_write_frequency_set(traj, step_save) != TNG_SUCCESS)
226 printf("Error setting writing frequency data. %s: %d\n",
227 __FILE__, __LINE__);
228 exit(1);
231 /* Write the first frame of box shape, positions, velocities and forces */
232 if(tng_util_box_shape_write(traj, 0, box_shape) != TNG_SUCCESS)
234 printf("Error writing box shape. %s: %d\n",
235 __FILE__, __LINE__);
236 exit(1);
238 if(tng_util_pos_write(traj, 0, pos) != TNG_SUCCESS)
240 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
241 exit(1);
243 if(tng_util_vel_write(traj, 0, vel) != TNG_SUCCESS)
245 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
246 exit(1);
248 if(tng_util_force_write(traj, 0, force) != TNG_SUCCESS)
250 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
251 exit(1);
254 wtime = omp_get_wtime ( );
256 for ( step = 1; step < step_num; step++ )
258 compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
260 if ( step == step_print )
262 printf ( " %8d %14f %14f %14e\n", step, potential, kinetic,
263 ( potential + kinetic - e0 ) / e0 );
264 step_print_index++;
265 step_print = ( step_print_index * step_num ) / step_print_num;
267 if(step % step_save == 0)
269 /* Write box shape, positions, velocities and forces */
270 if(tng_util_box_shape_write(traj, step, box_shape) != TNG_SUCCESS)
272 printf("Error writing box shape. %s: %d\n",
273 __FILE__, __LINE__);
274 exit(1);
276 if(tng_util_pos_write(traj, step, pos) != TNG_SUCCESS)
278 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
279 break;
281 if(tng_util_vel_write(traj, step, vel) != TNG_SUCCESS)
283 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
284 break;
286 if(tng_util_force_write(traj, step, force) != TNG_SUCCESS)
288 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
289 break;
292 update ( np, nd, pos, vel, force, acc, mass, dt );
294 wtime = omp_get_wtime ( ) - wtime;
296 printf ( "\n" );
297 printf ( " Elapsed time for main computation:\n" );
298 printf ( " %f seconds.\n", wtime );
300 free ( acc );
301 free ( box );
302 free ( box_shape );
303 free ( force );
304 free ( pos );
305 free ( vel );
307 /* Close the TNG output. */
308 tng_util_trajectory_close(&traj);
310 printf ( "\n" );
311 printf ( "MD_OPENMP\n" );
312 printf ( " Normal end of execution.\n" );
314 printf ( "\n" );
315 timestamp ( );
317 return 0;
319 /******************************************************************************/
321 void compute ( int np, int nd, float pos[], float vel[],
322 float mass, float f[], float *pot, float *kin )
324 /******************************************************************************/
326 Purpose:
328 COMPUTE computes the forces and energies.
330 Discussion:
332 The computation of forces and energies is fully parallel.
334 The potential function V(X) is a harmonic well which smoothly
335 saturates to a maximum value at PI/2:
337 v(x) = ( sin ( min ( x, PI2 ) ) )**2
339 The derivative of the potential is:
341 dv(x) = 2.0 * sin ( min ( x, PI2 ) ) * cos ( min ( x, PI2 ) )
342 = sin ( 2.0 * min ( x, PI2 ) )
344 Licensing:
346 This code is distributed under the GNU LGPL license.
348 Modified:
350 21 November 2007
352 Author:
354 Original FORTRAN77 version by Bill Magro.
355 C version by John Burkardt.
357 Parameters:
359 Input, int NP, the number of particles.
361 Input, int ND, the number of spatial dimensions.
363 Input, float POS[ND*NP], the position of each particle.
365 Input, float VEL[ND*NP], the velocity of each particle.
367 Input, float MASS, the mass of each particle.
369 Output, float F[ND*NP], the forces.
371 Output, float *POT, the total potential energy.
373 Output, float *KIN, the total kinetic energy.
376 float d;
377 float d2;
378 int i;
379 int j;
380 int k;
381 float ke;
382 float pe;
383 float PI2 = 3.141592653589793 / 2.0;
384 float rij[3];
386 pe = 0.0;
387 ke = 0.0;
389 # pragma omp parallel \
390 shared ( f, nd, np, pos, vel ) \
391 private ( i, j, k, rij, d, d2 )
394 # pragma omp for reduction ( + : pe, ke )
395 for ( k = 0; k < np; k++ )
398 Compute the potential energy and forces.
400 for ( i = 0; i < nd; i++ )
402 f[i+k*nd] = 0.0;
405 for ( j = 0; j < np; j++ )
407 if ( k != j )
409 d = dist ( nd, pos+k*nd, pos+j*nd, rij );
411 Attribute half of the potential energy to particle J.
413 if ( d < PI2 )
415 d2 = d;
417 else
419 d2 = PI2;
422 pe = pe + 0.5 * pow ( sin ( d2 ), 2 );
424 for ( i = 0; i < nd; i++ )
426 f[i+k*nd] = f[i+k*nd] - rij[i] * sin ( 2.0 * d2 ) / d;
431 Compute the kinetic energy.
433 for ( i = 0; i < nd; i++ )
435 ke = ke + vel[i+k*nd] * vel[i+k*nd];
439 ke = ke * 0.5 * mass;
441 *pot = pe;
442 *kin = ke;
444 return;
446 /******************************************************************************/
448 float dist ( int nd, float r1[], float r2[], float dr[] )
450 /******************************************************************************/
452 Purpose:
454 DIST computes the displacement (and its norm) between two particles.
456 Licensing:
458 This code is distributed under the GNU LGPL license.
460 Modified:
462 21 November 2007
464 Author:
466 Original FORTRAN77 version by Bill Magro.
467 C version by John Burkardt.
469 Parameters:
471 Input, int ND, the number of spatial dimensions.
473 Input, float R1[ND], R2[ND], the positions of the particles.
475 Output, float DR[ND], the displacement vector.
477 Output, float D, the Euclidean norm of the displacement.
480 float d;
481 int i;
483 d = 0.0;
484 for ( i = 0; i < nd; i++ )
486 dr[i] = r1[i] - r2[i];
487 d = d + dr[i] * dr[i];
489 d = sqrt ( d );
491 return d;
493 /******************************************************************************/
495 void initialize ( int np, int nd, float box[], int *seed, float pos[],
496 float vel[], float acc[] )
498 /******************************************************************************/
500 Purpose:
502 INITIALIZE initializes the positions, velocities, and accelerations.
504 Licensing:
506 This code is distributed under the GNU LGPL license.
508 Modified:
510 21 November 2007
512 Author:
514 Original FORTRAN77 version by Bill Magro.
515 C version by John Burkardt.
517 Parameters:
519 Input, int NP, the number of particles.
521 Input, int ND, the number of spatial dimensions.
523 Input, float BOX[ND], specifies the maximum position
524 of particles in each dimension.
526 Input, int *SEED, a seed for the random number generator.
528 Output, float POS[ND*NP], the position of each particle.
530 Output, float VEL[ND*NP], the velocity of each particle.
532 Output, float ACC[ND*NP], the acceleration of each particle.
535 int i;
536 int j;
538 Give the particles random positions within the box.
540 for ( i = 0; i < nd; i++ )
542 for ( j = 0; j < np; j++ )
544 pos[i+j*nd] = box[i] * r8_uniform_01 ( seed );
548 for ( j = 0; j < np; j++ )
550 for ( i = 0; i < nd; i++ )
552 vel[i+j*nd] = 0.0;
555 for ( j = 0; j < np; j++ )
557 for ( i = 0; i < nd; i++ )
559 acc[i+j*nd] = 0.0;
562 return;
564 /******************************************************************************/
566 float r8_uniform_01 ( int *seed )
568 /******************************************************************************/
570 Purpose:
572 R8_UNIFORM_01 is a unit pseudorandom R8.
574 Discussion:
576 This routine implements the recursion
578 seed = 16807 * seed mod ( 2**31 - 1 )
579 unif = seed / ( 2**31 - 1 )
581 The integer arithmetic never requires more than 32 bits,
582 including a sign bit.
584 Licensing:
586 This code is distributed under the GNU LGPL license.
588 Modified:
590 11 August 2004
592 Author:
594 John Burkardt
596 Reference:
598 Paul Bratley, Bennett Fox, Linus Schrage,
599 A Guide to Simulation,
600 Springer Verlag, pages 201-202, 1983.
602 Bennett Fox,
603 Algorithm 647:
604 Implementation and Relative Efficiency of Quasirandom
605 Sequence Generators,
606 ACM Transactions on Mathematical Software,
607 Volume 12, Number 4, pages 362-376, 1986.
609 Parameters:
611 Input/output, int *SEED, a seed for the random number generator.
613 Output, float R8_UNIFORM_01, a new pseudorandom variate, strictly between
614 0 and 1.
617 int k;
618 float r;
620 k = *seed / 127773;
622 *seed = 16807 * ( *seed - k * 127773 ) - k * 2836;
624 if ( *seed < 0 )
626 *seed = *seed + 2147483647;
629 r = ( float ) ( *seed ) * 4.656612875E-10;
631 return r;
633 /******************************************************************************/
635 void timestamp ( void )
637 /******************************************************************************/
639 Purpose:
641 TIMESTAMP prints the current YMDHMS date as a time stamp.
643 Example:
645 31 May 2001 09:45:54 AM
647 Licensing:
649 This code is distributed under the GNU LGPL license.
651 Modified:
653 24 September 2003
655 Author:
657 John Burkardt
659 Parameters:
661 None
664 # define TIME_SIZE 40
666 static char time_buffer[TIME_SIZE];
667 const struct tm *tm;
668 time_t now;
670 now = time ( NULL );
671 tm = localtime ( &now );
673 strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm );
675 printf ( "%s\n", time_buffer );
677 return;
678 # undef TIME_SIZE
680 /******************************************************************************/
682 void update ( int np, int nd, float pos[], float vel[], float f[],
683 float acc[], float mass, float dt )
685 /******************************************************************************/
687 Purpose:
689 UPDATE updates positions, velocities and accelerations.
691 Discussion:
693 The time integration is fully parallel.
695 A velocity Verlet algorithm is used for the updating.
697 x(t+dt) = x(t) + v(t) * dt + 0.5 * a(t) * dt * dt
698 v(t+dt) = v(t) + 0.5 * ( a(t) + a(t+dt) ) * dt
699 a(t+dt) = f(t) / m
701 Licensing:
703 This code is distributed under the GNU LGPL license.
705 Modified:
707 17 April 2009
709 Author:
711 Original FORTRAN77 version by Bill Magro.
712 C version by John Burkardt.
714 Parameters:
716 Input, int NP, the number of particles.
718 Input, int ND, the number of spatial dimensions.
720 Input/output, float POS[ND*NP], the position of each particle.
722 Input/output, float VEL[ND*NP], the velocity of each particle.
724 Input, float F[ND*NP], the force on each particle.
726 Input/output, float ACC[ND*NP], the acceleration of each particle.
728 Input, float MASS, the mass of each particle.
730 Input, float DT, the time step.
733 int i;
734 int j;
735 float rmass;
737 rmass = 1.0 / mass;
739 # pragma omp parallel \
740 shared ( acc, dt, f, nd, np, pos, rmass, vel ) \
741 private ( i, j )
743 # pragma omp for
744 for ( j = 0; j < np; j++ )
746 for ( i = 0; i < nd; i++ )
748 pos[i+j*nd] = pos[i+j*nd] + vel[i+j*nd] * dt + 0.5 * acc[i+j*nd] * dt * dt;
749 vel[i+j*nd] = vel[i+j*nd] + 0.5 * dt * ( f[i+j*nd] * rmass + acc[i+j*nd] );
750 acc[i+j*nd] = f[i+j*nd] * rmass;
755 return;
758 #endif