Skip to content

Precision loss when writing GSD files #2194

@CalCraven

Description

@CalCraven

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 precision

Maybe 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-06

Expected 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions