# Using a Model in a Simulation¶

import hoomd, hoomd.md
import hoomd.htf as htf

...hoomd initialization code...
model = MyModel(32)
tfcompute = htf.tfcompute(model)

nlist = hoomd.md.nlist.cell()
tfcompute.attach(nlist, r_cut=3)

...other hoomd code...

hoomd.run(...)


where MyModel is model you created following the steps in Building a Model, nlist is a hoomd neighbor list object and r_cut is the maximum distance to consider particles as being neighbors. nlist is optional and is not required if your graph doesn’t use the nlist object (you passed 0 as the first arg when building your graph).

## Logging¶

The default logging level of TensorFlow is relatively noisy. You can reduce the amount of logged statements via

import tensorflow as tf
tf.get_logger().setLevel('ERROR')


## Batching¶

If you do not use molecule batching when building your model (i.e., your model isn’t a sub class of :py:class: .MolSimModel, you can optionally split your batches to be smaller than the entire system. This is set via the batch_size integer argument to tfcompute.attach(). This can help for high-memory simulations where you cannot spare the GPU memory to have each tensor be the size of your system.

## Training¶

Training can be done while running your simulation where the labels are the HOOMD-blue forces. To do this, you must first compile your model as described in Keras documentation. For example,

model.compile('Adam', 'mean_squared_error')


will compile your model to use mean squared error on per-particle forces (note that the forces tensor contains energy in the last column) as the loss and the Adam optimizer. To train while running, just add the train = True arg.

tfcompute.attach(train=True)


You can also train less than each step (recommended):

tfcompute.attach(train=True, period=100)


## Model Output¶

By default, your model output is not saved except to send the forces (and possibly virial) to HOOMD-blue. You can have tfcompute capture your model output by adding save_output_period=100. In this case, output will be saved every 100 steps. Note that if your model is outputting forces as specified in the constructor of a SimModel, the forces will not be saved. Here is a complete example:

class MyModel(htf.SimModel):
def compute(self, nlist):
rinv = htf.nlist_rinv(nlist)
energy = rinv
forces = htf.compute_nlist_forces(nlist, energy)
avg_coord_number = tf.reduce_mean(tf.cast(rinv > 0, tf.float32))
return forces, energy, avg_coord_number

model = MyModel(16)
tfcompute = htf.tfcompute(model)
...
...
tfcompute.attach(nlist, rcut=5.0, save_output_period=100)
hoomd.run(1000)

output_energy = tfcompute.outputs[0]
output_avg_coord_number = tfcompute.outputs[1]


## Changing Model Object¶

If your SimModel.compute(nlist, positions, box) method depends on attributes of self, you have to call an SimModel.retrace_compute() if you updated these attributes. See an example

class MyModel(htf.SimModel):
def setup(self, s):
self.s = s
def compute(self, nlist):
rinv = htf.nlist_rinv(nlist)
energy = self.s * rinv
forces = htf.compute_nlist_forces(nlist, energy)
avg_coord_number = tf.reduce_mean(tf.cast(rinv > 0, tf.float32))
return forces, energy, avg_coord_number

model = MyModel(16)
tfcompute = htf.tfcompute(model)
...
...
tfcompute.attach(nlist, rcut=5.0, save_output_period=100)
hoomd.run(1000)

# Now I update s
model.s = 2.5
# I must call retrace_compute
model.retrace_compute()

homod.run(1000)