2022-04-08 15:42:20 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
2022-04-11 10:14:40 +00:00
|
|
|
################################################################################
|
|
|
|
### parse script parameters ###
|
|
|
|
################################################################################
|
|
|
|
from optparse import OptionParser
|
2022-04-08 15:42:20 +00:00
|
|
|
|
2022-04-11 10:14:40 +00:00
|
|
|
usage = "usage: %prog [options] [<NR_PARTICLES>] [<BOX_SIZE>] [<TEMPERATURE>]"
|
|
|
|
arg_parser = OptionParser(usage = usage)
|
2022-04-08 15:42:20 +00:00
|
|
|
arg_parser.add_option("-M", "--particles", action = "store", type = int,
|
2022-04-11 10:14:40 +00:00
|
|
|
dest = "nr_particles", default = -1,
|
|
|
|
help = "Nr. of particles (required!)")
|
2022-04-08 15:42:20 +00:00
|
|
|
arg_parser.add_option("-L", "--box_size", action = "store", type = float,
|
2022-04-11 10:14:40 +00:00
|
|
|
dest = "box_size", default = -1.0,
|
|
|
|
help = "side box_size [A (angstrom)] of the simulation cube (required!)")
|
|
|
|
arg_parser.add_option("-T", "--temperature", action = "store", type = float,
|
|
|
|
dest = "temperature", default = -1.0,
|
|
|
|
help = "temperature [K] to generate initial conditions (required!)")
|
|
|
|
arg_parser.add_option("-o", "--output", action = "store", type = str,
|
|
|
|
dest = "output", default = "task02.xyz",
|
|
|
|
help = "output file path (default: 'task02.xyz')")
|
|
|
|
arg_parser.add_option("-v", action = "store_true",
|
|
|
|
dest = "verbose", default = False,
|
|
|
|
help = "turn verbosity mode on (default: off a.k.a. silent)")
|
2022-04-08 15:42:20 +00:00
|
|
|
# Parse command line arguments (as def. above) or store defaults to `config`
|
|
|
|
config, args = arg_parser.parse_args()
|
2022-04-11 10:14:40 +00:00
|
|
|
# overwrite options with positional arguments if supplied
|
|
|
|
try:
|
|
|
|
if len(args) > 0:
|
|
|
|
config.nr_particles = int(args[0])
|
|
|
|
if len(args) > 1:
|
|
|
|
config.box_size = float(args[1])
|
|
|
|
if len(args) > 2:
|
|
|
|
config.temperature = float(args[2])
|
|
|
|
except ValueError as expression:
|
|
|
|
arg_parser.print_help()
|
|
|
|
print(f"Error: {expression}")
|
|
|
|
exit(-1)
|
|
|
|
else:
|
|
|
|
# quick and dirty validation
|
|
|
|
if not config.nr_particles > 0 \
|
|
|
|
or not config.box_size > 0.0 \
|
|
|
|
or not config.temperature > 0.0:
|
|
|
|
arg_parser.print_help()
|
|
|
|
print("Error: missing or illegal argument")
|
|
|
|
exit(-1)
|
2022-04-08 15:42:20 +00:00
|
|
|
|
2022-04-11 10:14:40 +00:00
|
|
|
################################################################################
|
|
|
|
### task 2 / generation of initial conditions ###
|
|
|
|
################################################################################
|
|
|
|
# note, load module _after_ processing script parameters (no need to load all
|
|
|
|
# of the heavy numeric modules if only usage or alike is needed)
|
|
|
|
import numpy as np
|
|
|
|
import scipy
|
|
|
|
from jax import jit, grad
|
|
|
|
from molecular_dynamics import dump, energy, force, mass
|
2022-04-08 15:42:20 +00:00
|
|
|
|
|
|
|
# Sample random positions in a 3D cube (TODO: make this not just uniform :-})
|
|
|
|
position = np.random.uniform(0.0, config.box_size, (config.nr_particles, 3))
|
|
|
|
|
|
|
|
# Sample particle velocities
|
2022-04-11 10:14:40 +00:00
|
|
|
sd = np.sqrt(scipy.constants.Boltzmann * config.temperature / mass)
|
2022-04-08 15:42:20 +00:00
|
|
|
velocity = np.random.normal(0.0, sd, (config.nr_particles, 3))
|
|
|
|
# center velocities
|
|
|
|
velocity -= velocity.mean(axis = 0)
|
|
|
|
|
|
|
|
# remember energy before optimizing for a low energy state
|
2022-04-11 10:14:40 +00:00
|
|
|
initial_energy = energy(position, config.box_size)
|
|
|
|
forces = force(position, config.box_size)
|
|
|
|
initial_mean_forces = forces.mean(axis = 0)
|
|
|
|
initial_mean_fnorm = np.linalg.norm(forces, axis = 1).mean()
|
2022-04-08 15:42:20 +00:00
|
|
|
|
|
|
|
# optimize energy to find low energy system state using Conjugate-Gradients
|
2022-04-11 10:14:40 +00:00
|
|
|
optim = scipy.optimize.minimize(energy, # objective func.
|
|
|
|
jac = jit(grad(energy)), # jacobian
|
|
|
|
x0 = position, # initial position
|
|
|
|
args = (config.box_size, ), # further args
|
|
|
|
method = "CG")
|
2022-04-08 15:42:20 +00:00
|
|
|
# extract (and reshape) optimization result
|
|
|
|
position = optim.x.reshape((config.nr_particles, 3))
|
2022-04-11 10:14:40 +00:00
|
|
|
# ensure all particles are in the box
|
|
|
|
position = np.mod(position, config.box_size)
|
2022-04-08 15:42:20 +00:00
|
|
|
|
|
|
|
# recompute stats after optimizing for low energy state
|
2022-04-11 10:14:40 +00:00
|
|
|
final_energy = energy(position, config.box_size)
|
|
|
|
forces = force(position, config.box_size)
|
|
|
|
final_mean_forces = forces.mean(axis = 0)
|
|
|
|
final_mean_fnorm = np.linalg.norm(forces, axis = 1).mean()
|
2022-04-08 15:42:20 +00:00
|
|
|
|
|
|
|
# store state snapshot to file (default target file defined by script args)
|
2022-04-11 10:14:40 +00:00
|
|
|
dump(config.output, position, velocity, config.box_size)
|
2022-04-08 15:42:20 +00:00
|
|
|
|
|
|
|
# report stats (if requested by `-v` script argument)
|
|
|
|
if config.verbose:
|
2022-04-11 10:14:40 +00:00
|
|
|
print(f"Initial Energy: {initial_energy:.4e}")
|
|
|
|
print( "Initial Mean Forces: {:.4e} {:.4e} {:.4e}".format(*initial_mean_forces))
|
|
|
|
print(f"Initial Mean ||Forces||: {initial_mean_fnorm:.4e}")
|
|
|
|
print(f"Final Energy: {final_energy:.4e}")
|
|
|
|
print( "Final Mean Forces: {:.4e} {:.4e} {:.4e}".format(*final_mean_forces))
|
|
|
|
print(f"Final Mean ||Forces||: {final_mean_fnorm:.4e}")
|
|
|
|
print(f"Done: saved inital state to '{config.output}'")
|