Post-run analysis#

In the last tutorial, we simulated a Lennard-Jones crystal and stored data in the file 'LJ_T0.70.h5'. In this tutorial, we will analyse this data. Below, we will do some standard imports and load the data from the file on the disk.

# Imports
import numpy as np
import matplotlib.pyplot as plt
import h5py

import gamdpy as gp

# Select h5 file
filename='./LJ_T0.70.h5'
output = gp.tools.TrajectoryIO(filename).get_h5()
Found .h5 file (./LJ_T0.70.h5), loading to gamdpy as output dictionary

Brief about the h5 data file#

Large data sets#

Large data sets, such as the trajectory of particle positions, are stored as an HDF5 dataset (a data structure similar to a NumPy array, but data is stored on the disk).

nblocks, nconfs, N, D = output['trajectory_saver/positions'].shape
print(f'Number of timeblocks:          {nblocks = }')
print(f'Configurations per timeblock:  {nconfs = }')
print(f'Number of particles:           {N = }')
print(f'Number of spatial dimensions:  {D = }')
Number of timeblocks:          nblocks = 32
Configurations per timeblock:  nconfs = 12
Number of particles:           N = 2048
Number of spatial dimensions:  D = 3

Information in attributes#

Some information, such as details about the simulation box, is stored as attributes. Below, we fetch such data to get the density of the NVT simulation.

simbox = output['initial_configuration'].attrs['simbox_data']
volume = np.prod(simbox)
rho = N/volume
print(f'Density:  {rho = }')
Density:  rho = np.float32(0.97300005)

Thermodynamics#

The gamdpy package contains helper functions to read data in .h5 files. Thermodynamic data, stored in the 'scalar_saver' group, can be extracted using the gp.extract_scalars() function (we skip the first block with first_block=1 since the system is not equilibrated).

# Extract thermodynamic data
U, W, K = gp.extract_scalars(output, ['U', 'W', 'K'], first_block=1)

# Get times
dt = output.attrs['dt']  # Timestep
time = np.arange(len(U)) * dt * output['scalar_saver'].attrs['steps_between_output']

# Plot potential energy per particle as a function of time
plt.figure()
plt.plot(time, U/N)
plt.xlabel(r'Time, $t$')
plt.ylabel('Potential energy per particle, $u=U/N$')
plt.show()
../_images/54da6844d849e0fa8c2e7ce6aeaeafbeb909c3c3f4b51a5251f03a1f2039dd4d.png

Some properties need to be computed from the data stored. Examples are the kinetic temperature and pressure (in LJ units).

# Compute instantaneous kinetic temperature
dof = D * N - D  # degrees of freedom
T_kin = 2 * K / dof

# Compute instantaneous pressure
P = rho * T_kin + W / volume

print(f'Mean kinetic temperature:  {np.mean(T_kin) = }')
print(f'Mean pressure:             {np.mean(P) = }')
Mean kinetic temperature:  np.mean(T_kin) = np.float32(0.699899)
Mean pressure:             np.mean(P) = np.float32(1.4027747)

Structure#

To investigate the structure and dynamics of particles, we need to analyse data in the 'trajectory_saver' group. Again, gamdpy has some built-in functionality to compute various measures. Let us calculate the radial distribution function. We will do this by inserting positions into a configuration object, and using the gamdpy.CalculatorRadialDistribution() class. The calculation is done on the GPU (for efficency).

# Create configuration object
configuration = gp.Configuration(D=D, N=N)
configuration.simbox = gp.Orthorhombic(D, output['initial_configuration'].attrs['simbox_data'])
configuration.ptype = output['initial_configuration/ptype']
configuration.copy_to_device()

# Call the radial distribution (RDF) calculator
calc_rdf = gp.CalculatorRadialDistribution(configuration, bins=300)

# Loop positions and compute the RDF
positions = output['trajectory_saver/positions'][:,:,:,:]
positions = positions.reshape(nblocks*nconfs,N,D)

# Loop over the last configurations in each time block
skipped_timeblocks = 1
start = nconfs-1+skipped_timeblocks*nconfs
step = nconfs
for pos in positions[start::step]:
    configuration['r'] = pos
    configuration.copy_to_device()
    calc_rdf.update()
rdf_data = calc_rdf.read()

# Plot RDF
plt.figure()
plt.plot(rdf_data['distances'], rdf_data['rdf'][0])
plt.xlabel(r'Pair distance, $r_{ij}$')
plt.ylabel('Radial Distribution Function')
plt.savefig(filename+'_rdf.pdf')
plt.show()
../_images/0e3abb1fbd4bcab05e7d09c7b59b34517266324d5ff7fdbf1cf75cb90ee64aec.png

Dynamics#

There are also built-in tools for analysing the trajectory. For example, the gamdpy.tools.calc_dynamics function computes several dynamical measures, including the mean squared displacement and the intermediate scattering function.

qvalues = 7.5
dynamics = gp.tools.calc_dynamics(output, first_block=1, qvalues=qvalues)  # Dictionary with dynamics
dynamics.keys()
dict_keys(['times', 'msd', 'alpha2', 'qvalues', 'Fs', 'count'])
plt.figure()
plt.plot(dynamics['times'], dynamics['msd'], 'o')
plt.xscale('log')
plt.ylim(0, None)
plt.xlabel(r'Time, $t$')
plt.ylabel(r'Mean squared displacement')
plt.show()
../_images/eb8d85f1455bd558444407cb9f4175d6e616c0af94a1215ba969e10efd534e7d.png
plt.figure()
plt.plot(dynamics['times'], dynamics['Fs'], 'o')
plt.xscale('log')
plt.ylim(0, 1.1)
plt.xlabel(r'Time, $t$')
plt.ylabel(r'Intermediate scattering function, $F(t, q = ' f'{qvalues}' r'$)')
plt.show()
../_images/f3b99dfc3449f153785a90b5de9942d342c209b09fbda50891a3138ec359fab3.png

Details about stored data#

Structure of h5 file#

Below is a function to print the structure of a given h5 file.

gp.tools.print_h5_structure(output)
initial_configuration/ (Group)
    ptype  (Dataset, shape=(2048,), dtype=int32)
    r_im  (Dataset, shape=(2048, 3), dtype=int32)
    scalars  (Dataset, shape=(2048, 4), dtype=float32)
    topology/ (Group)
        angles  (Dataset, shape=(0,), dtype=int32)
        bonds  (Dataset, shape=(0,), dtype=int32)
        dihedrals  (Dataset, shape=(0,), dtype=int32)
        molecules/ (Group)
    vectors  (Dataset, shape=(3, 2048, 3), dtype=float32)
scalar_saver/ (Group)
    scalars  (Dataset, shape=(32, 64, 3), dtype=float32)
trajectory_saver/ (Group)
    images  (Dataset, shape=(32, 12, 2048, 3), dtype=int32)
    positions  (Dataset, shape=(32, 12, 2048, 3), dtype=float32)

Attributes inside the H5 file#

Below is a function that will print the attributes in a given h5 file.

gp.tools.print_h5_attributes(output)
Attributes at /:
    - dt: 0.005
    - script_content: ...
    - script_name: ...
Attributes at /initial_configuration/:
    - simbox_data: [12.815602 12.815602 12.815602]
    - simbox_name: Orthorhombic
Attributes at /initial_configuration/scalars:
    - scalar_columns: ['U' 'W' 'K' 'm']
Attributes at /initial_configuration/topology/molecules/:
    - names: []
Attributes at /initial_configuration/vectors:
    - vector_columns: ['r' 'v' 'f']
Attributes at /scalar_saver/:
    - scalar_names: ['U' 'W' 'K']
    - steps_between_output: 16

Concluding remarks#

You have now conducted your first simulation and done some post-analysis of the trajectory. You now understand the basics of how gamdpy works, and are ready for more advanced simulations and analysis - see examples.