-
Notifications
You must be signed in to change notification settings - Fork 154
Description
Description
When using charge normalization for elementary_charges, floating point errors result in total charges that don't quite cancel out, something close to 1e-17. I think the primary cause for this is the charge factor, 0.0848385920 e, which is pretty much unavoidable.
This small of a rounding issue doesn't seem to cause a problem, unless the snapshot is written to a gsd file, and then loaded back into a simulation using sim.create_state_from_gsd. The total charge jumps from 1e-16 to 1e-7, and this can cause charge issues in the simulation. I especially see this when I run a system with >5000 molecules, which should be charge neutral, but end up with a net charge.
I've tried using the Decimal to improve the float precision to set to 64 bit precision during the initial conversion from the elementary_charge units to the dimensionless units, but saw no change in behavior.
from decimal import Decimal, getcontext
getcontext().prec = 64 # Set desired precisionMaybe there should be a way to charge balance the hoomd state once something is loaded in? Even just shifting every charge by the factor of total_charge/sim.state.N_particles might counteract issues like this?
Script
import hoomd
import gsd.hoomd
import numpy as np
print(f"Hoomd version: {hoomd.version.version}")
print(f"GSD version: {gsd.version.version}")
# NOTE: first charges fails as well, but has more particles so simplified to just 4 particles.
# charges = [-0.18, -0.06, -0.18, 0.51, -0.43, -0.33, 0.16, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.03, 0.03, 0.03]
charges = [-0.1, 0.1/3, 0.1/3, 0.1/3]
print(f"Initial float sum for charges with single molecule: {sum(charges)}")
charge_factor = 0.0848385920 # from https://hoomd-blue.readthedocs.io/en/latest/units.html
print(f"Initial normalized sum for charges with single molecule: {sum(charges)/charge_factor}")
snapshot = gsd.hoomd.Frame()
box = [10, 10, 10, 0, 0, 0] # Lx, Ly, Lz, xy, xz, yz
n_mols = 100
snapshot.particles.N = len(charges)*n_mols # replicates highlight charge issue
positions = (np.random.rand(snapshot.particles.N , 3) - 0.5) * 10
snapshot.particles.position = positions
all_charges = np.array(charges*n_mols) /charge_factor
print(f"Final charges plugged in: {sum(all_charges)}")
snapshot.particles.charge = all_charges
print(f"Ending total charge {sum(snapshot.particles.charge)}")
# NOTE: fill in meaningless data
# Define particle types
snapshot.particles.types = ['A']
# Assign type 'A' (typeid 0) to all particles
snapshot.particles.typeid = [0] * snapshot.particles.N
# save to gsd
device = hoomd.device.CPU()
sim = hoomd.Simulation(device=device, seed=1)
sim.create_state_from_snapshot(snapshot)
hoomd.write.GSD.write(
state=sim.state, filename="checkcharge.gsd", mode="wb"
)
del sim
# reload gsd
device = hoomd.device.CPU()
sim = hoomd.Simulation(device=device, seed=1)
sim.create_state_from_gsd("checkcharge.gsd")
snap_loaded = sim.state.get_snapshot()
print(f"Reloaded charge: {sum(snap_loaded.particles.charge)}")Input files
No response
Output
Hoomd version: 6.0.0
GSD version: 4.2.0
Initial float sum for charges with single molecule: -6.938893903907228e-18
Initial normalized sum for charges with single molecule: -8.178935718201485e-17
Final charges plugged in: -1.1102230246251565e-16
Ending total charge -1.1102230246251565e-16
Reloaded charge: -8.940696716308594e-06Expected output
Hoomd version: 6.0.0
GSD version: 4.2.0
Initial float sum for charges with single molecule: -6.938893903907228e-18
Initial normalized sum for charges with single molecule: -8.178935718201485e-17
Final charges plugged in: -1.1102230246251565e-16
Ending total charge -1.1102230246251565e-16
Reloaded charge: -1.1102230246251565e-16
Platform
macOS, CPU
Installation method
Conda-forge package
HOOMD-blue version
6.0.0
Python version
3.12.8