def test_ron(simulation_factory, two_particle_snapshot_factory): lj = md.pair.LJ(nlist=md.nlist.Cell(buffer=0.4), mode='xplor', default_r_cut=2.5) lj.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} with pytest.raises(TypeConversionError): lj.r_on[('A', 'A')] = 'str' with pytest.raises(TypeConversionError): lj.r_on[('A', 'A')] = [1, 2, 3] sim = simulation_factory(two_particle_snapshot_factory(dimensions=3, d=.5)) integrator = md.Integrator(dt=0.005) integrator.forces.append(lj) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator assert lj.r_on.to_base() == {} lj.r_on[('A', 'A')] = 1.5 assert _equivalent_data_structures({('A', 'A'): 1.5}, lj.r_on.to_base()) sim.run(0) assert _equivalent_data_structures({('A', 'A'): 1.5}, lj.r_on.to_base()) lj.r_on[('A', 'A')] = 1.0 assert _equivalent_data_structures({('A', 'A'): 1.0}, lj.r_on.to_base())
def simulation(simulation_factory, two_particle_snapshot_factory): sim = simulation_factory(two_particle_snapshot_factory()) integrator = md.Integrator(0.005) integrator.methods.append(md.methods.NVE(hoomd.filter.All())) sim.operations += integrator sim.state.thermalize_particle_momenta(hoomd.filter.All(), kT=1.0) return sim
def test_running_simulation(simulation_factory, two_particle_snapshot_factory, valid_body_definition): rigid = md.constrain.Rigid() rigid.body["A"] = valid_body_definition langevin = md.methods.Langevin(kT=2.0, filter=hoomd.filter.Rigid()) lj = hoomd.md.pair.LJ(nlist=md.nlist.Cell(buffer=0.4), mode="shift") lj.params.default = {"epsilon": 0.0, "sigma": 1} lj.params[("A", "A")] = {"epsilon": 1.0} lj.params[("B", "B")] = {"epsilon": 1.0} lj.r_cut.default = 2**(1.0 / 6.0) integrator = md.Integrator(dt=0.005, methods=[langevin], forces=[lj]) integrator.rigid = rigid initial_snapshot = two_particle_snapshot_factory() if initial_snapshot.communicator.rank == 0: initial_snapshot.particles.types = ["A", "B"] sim = simulation_factory(initial_snapshot) sim.seed = 5 rigid.create_bodies(sim.state) sim.operations += integrator sim.run(5) snapshot = sim.state.get_snapshot() if sim.device.communicator.rank == 0: check_bodies(snapshot, valid_body_definition)
def test_setting_body_after_attaching(simulation_factory, two_particle_snapshot_factory, valid_body_definition): """Test updating body definition without updating sim particles fails.""" rigid = md.constrain.Rigid() langevin = md.methods.Langevin(kT=2.0, filter=hoomd.filter.Rigid()) lj = hoomd.md.pair.LJ(nlist=md.nlist.Cell(buffer=0.4), mode="shift") lj.params.default = {"epsilon": 0.0, "sigma": 1} lj.params[("A", "A")] = {"epsilon": 1.0} lj.params[("B", "B")] = {"epsilon": 1.0} lj.r_cut.default = 2**(1.0 / 6.0) integrator = md.Integrator(dt=0.005, methods=[langevin], forces=[lj]) integrator.rigid = rigid initial_snapshot = two_particle_snapshot_factory() if initial_snapshot.communicator.rank == 0: initial_snapshot.particles.types = ["A", "B"] sim = simulation_factory(initial_snapshot) sim.seed = 5 sim.operations += integrator sim.run(1) rigid.body["A"] = valid_body_definition # This should error because the bodies have not been updated, but the # setting should be fine. with pytest.raises(RuntimeError): sim.run(1)
def test_force_energy_accuracy(simulation_factory, two_particle_snapshot_factory, forces_and_energies): pot = forces_and_energies.pair_potential(**forces_and_energies.extra_args, nlist=md.nlist.Cell(), r_cut=2.5, mode='none') pot.params[('A', 'A')] = forces_and_energies.pair_potential_params snap = two_particle_snapshot_factory(particle_types=['A'], d=0.75) _update_snap(forces_and_energies.pair_potential, snap) sim = simulation_factory(snap) integrator = md.Integrator(dt=0.005) integrator.forces.append(pot) integrator.methods.append(md.methods.Langevin(hoomd.filter.All(), kT=1, seed=1)) sim.operations.integrator = integrator sim.run(0) particle_distances = [0.75, 1.5] for i in range(len(particle_distances)): d = particle_distances[i] r = np.array([0, 0, d]) / d snap = sim.state.snapshot if snap.exists: snap.particles.position[0] = [0, 0, .1] snap.particles.position[1] = [0, 0, d + .1] sim.state.snapshot = snap sim_energies = sim.operations.integrator.forces[0].energies sim_forces = sim.operations.integrator.forces[0].forces if sim_energies is not None: assert isclose(sum(sim_energies), forces_and_energies.energies[i]) assert isclose(sim_forces[0], forces_and_energies.forces[i] * r) assert isclose(sim_forces[0], -forces_and_energies.forces[i] * r)
def test_run(simulation_factory, lattice_snapshot_factory, valid_params): pair_keys = valid_params.pair_potential_params.keys() particle_types = list(set(itertools.chain.from_iterable(pair_keys))) pot = valid_params.pair_potential(**valid_params.extra_args, nlist=md.nlist.Cell(), r_cut=2.5, mode='none') pot.params = valid_params.pair_potential_params snap = lattice_snapshot_factory(particle_types=particle_types, n=7, a=1.7, r=0.01) if snap.exists: snap.particles.typeid[:] = np.random.randint(0, len(snap.particles.types), snap.particles.N) sim = simulation_factory(snap) integrator = md.Integrator(dt=0.005) integrator.forces.append(pot) integrator.methods.append(md.methods.Langevin(hoomd.filter.All(), kT=1, seed=1)) sim.operations.integrator = integrator sim.run(0) for nsteps in [3, 5, 10]: old_snap = sim.state.snapshot sim.run(nsteps) new_snap = sim.state.snapshot if new_snap.exists: assert not np.allclose(new_snap.particles.position, old_snap.particles.position)
def test_custom_filter(make_filter_snapshot, simulation_factory): """Tests that custom particle filters work on simulations. Specifically we test that using the Langevin integrator method, that only particles selected by the custom filter move. Since the Langevin method uses random movements we don't need to initialize velocities or have any forces to test this. """ class NegativeCharge(CustomFilter): """Grab all particles with a negative charge.""" def __call__(self, state): with state.cpu_local_snapshot as snap: return snap.particles.tag[snap.particles.charge < 0] def __hash__(self): return hash(self.__class__.__name__) def __eq__(self, other): return isinstance(other, self.__class__) charge_filter = NegativeCharge() sim = simulation_factory(make_filter_snapshot()) # grabs tags on individual MPI ranks with sim.state.cpu_local_snapshot as snap: # Grab half of all particles on an MPI rank, 1 particle, or no particles # depending on how many particles are local to the MPI ranks. local_Np = snap.particles.charge.shape[0] N_negative_charge = max(0, max(1, int(local_Np * 0.5))) negative_charge_ind = np.random.choice(local_Np, N_negative_charge, replace=False) # Get the expected tags returned by the custom filter and the positions # that should vary and remain static for testing after running. snap.particles.charge[negative_charge_ind] = -1.0 expected_tags = snap.particles.tag[negative_charge_ind] positive_charge_tags = snap.particles.tag[snap.particles.charge > 0] positive_charge_ind = snap.particles.rtag[positive_charge_tags] original_positions = snap.particles.position[negative_charge_ind] static_positions = snap.particles.position[positive_charge_ind] # Test that the filter merely works as expected and that tags are correctly # grabbed on local MPI ranks assert all(np.sort(charge_filter(sim.state)) == np.sort(expected_tags)) # Test that the filter works when used in a simulation langevin = md.methods.Langevin(charge_filter, 1.0) sim.operations += md.Integrator(0.005, methods=[langevin]) sim.run(100) snap = sim.state.snapshot if snap.communicator.rank == 0: assert not np.allclose(snap.particles.position[negative_charge_ind], original_positions) assert np.allclose(snap.particles.position[positive_charge_tags], static_positions)
def make_sim(force_obj, snapshot=None, domain_decomposition=None): sim = simulation_factory(snapshot, domain_decomposition) npt = md.methods.NPT(hoomd.filter.All(), kT=1, tau=1, S=1, tauS=1, couple="none") integrator = md.Integrator(dt=0.005, forces=[force_obj], methods=[npt]) sim.operations.integrator = integrator return sim
def test_mode(simulation_factory, two_particle_snapshot_factory, mode): cell = md.nlist.Cell() lj = md.pair.LJ(nlist=cell, r_cut=2.5, mode=mode) lj.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} snap = two_particle_snapshot_factory(dimensions=3, d=.5) sim = simulation_factory(snap) integrator = md.Integrator(dt=0.005) integrator.forces.append(lj) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator sim.run(1)
def test_error_on_invalid_body(simulation_factory, two_particle_snapshot_factory, valid_body_definition): rigid = md.constrain.Rigid() rigid.body["A"] = valid_body_definition langevin = md.methods.Langevin(kT=2.0, filter=hoomd.filter.Rigid()) integrator = md.Integrator(dt=0.005, methods=[langevin]) integrator.rigid = rigid initial_snapshot = two_particle_snapshot_factory() if initial_snapshot.communicator.rank == 0: initial_snapshot.particles.types = ["A", "B"] sim = simulation_factory(initial_snapshot) sim.operations += integrator with pytest.raises(RuntimeError): sim.run(0)
def test_failure_with_cpu_device_and_gpu_buffer(): """Assert we cannot access gpu buffers with a cpu_device.""" device = hoomd.device.CPU() snap = _make_two_particle_snapshot(device) sim = hoomd.Simulation(device) sim.create_state_from_snapshot(snap) custom_force = MyForce('gpu_local_force_arrays') npt = md.methods.NPT(hoomd.filter.All(), kT=1, tau=1, S=1, tauS=1, couple="none") integrator = md.Integrator(dt=0.005, forces=[custom_force], methods=[npt]) sim.operations.integrator = integrator with pytest.raises(RuntimeError): sim.run(1)
def test_force_energy_relationship(simulation_factory, two_particle_snapshot_factory, valid_params): # don't really test DPD and DPDLJ for this test pot_name = valid_params.pair_potential.__name__ if any(pot_name == name for name in ["DPD", "DPDLJ"]): pytest.skip("Cannot test force energy relationship for " + pot_name + " pair force") pair_keys = valid_params.pair_potential_params.keys() particle_types = list(set(itertools.chain.from_iterable(pair_keys))) pot = valid_params.pair_potential(**valid_params.extra_args, nlist=md.nlist.Cell(buffer=0.4), default_r_cut=2.5) for pair in valid_params.pair_potential_params: pot.params[pair] = valid_params.pair_potential_params[pair] if pot_name == 'DLVO': pot.r_cut[pair] = 2.5 - ((0.2 + 0.5) / 2 - 1) snap = two_particle_snapshot_factory(particle_types=particle_types, d=1.5) _update_snap(valid_params.pair_potential, snap) sim = simulation_factory(snap) _skip_if_triplet_gpu_mpi(sim, valid_params.pair_potential) integrator = md.Integrator(dt=0.005) integrator.forces.append(pot) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator sim.run(0) for pair in valid_params.pair_potential_params: snap = sim.state.get_snapshot() if snap.communicator.rank == 0: snap.particles.typeid[0] = particle_types.index(pair[0]) snap.particles.typeid[1] = particle_types.index(pair[1]) sim.state.set_snapshot(snap) calculated_forces = _calculate_force(sim) sim_forces = sim.operations.integrator.forces[0].forces if sim_forces is not None: np.testing.assert_allclose(calculated_forces[0], sim_forces[0], rtol=1e-05) np.testing.assert_allclose(calculated_forces[1], sim_forces[1], rtol=1e-05)
def test_force_energy_relationship(simulation_factory, two_particle_snapshot_factory, valid_params): # don't really test DPD and DPDLJ for this test pot_name = valid_params.pair_potential.__name__ if any(pot_name == name for name in ["DPD", "DPDLJ"]): pytest.skip("Cannot test force energy relationship for " + pot_name + " pair force") pair_keys = valid_params.pair_potential_params.keys() particle_types = list(set(itertools.chain.from_iterable(pair_keys))) pot = valid_params.pair_potential(**valid_params.extra_args, nlist=md.nlist.Cell(), r_cut=2.5, mode='none') for pair in valid_params.pair_potential_params: pot.params[pair] = valid_params.pair_potential_params[pair] snap = two_particle_snapshot_factory(particle_types=particle_types, d=1.5) _update_snap(valid_params.pair_potential, snap) sim = simulation_factory(snap) integrator = md.Integrator(dt=0.005) integrator.forces.append(pot) integrator.methods.append(md.methods.Langevin(hoomd.filter.All(), kT=1, seed=1)) sim.operations.integrator = integrator sim.run(0) for pair in valid_params.pair_potential_params: snap = sim.state.snapshot if snap.exists: snap.particles.typeid[0] = particle_types.index(pair[0]) snap.particles.typeid[1] = particle_types.index(pair[1]) sim.state.snapshot = snap calculated_forces = _calculate_force(sim) sim_forces = sim.operations.integrator.forces[0].forces if sim_forces is not None: np.testing.assert_allclose(calculated_forces[0], sim_forces[0], rtol=1e-06) np.testing.assert_allclose(calculated_forces[1], sim_forces[1], rtol=1e-06)
def test_run(simulation_factory, lattice_snapshot_factory, pair_potential): snap = lattice_snapshot_factory(particle_types=['A', 'B'], n=7, a=1.7, r=0.01) if snap.exists: snap.particles.typeid[:] = np.random.randint(0, len(snap.particles.types), snap.particles.N) sim = simulation_factory(snap) integrator = md.Integrator(dt=0.005) integrator.forces.append(pair_potential) integrator.methods.append(md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator for nsteps in [3, 5, 10]: old_snap = sim.state.snapshot sim.run(nsteps) new_snap = sim.state.snapshot if new_snap.exists: assert not np.allclose(new_snap.particles.position, old_snap.particles.position)
def test_running_without_body_definition(simulation_factory, two_particle_snapshot_factory): rigid = md.constrain.Rigid() langevin = md.methods.Langevin(kT=2.0, filter=hoomd.filter.Rigid()) lj = hoomd.md.pair.LJ(nlist=md.nlist.Cell(), mode="shift") lj.params.default = {"epsilon": 0.0, "sigma": 1} lj.params[("A", "A")] = {"epsilon": 1.0} lj.params[("B", "B")] = {"epsilon": 1.0} lj.r_cut.default = 2**(1.0 / 6.0) integrator = md.Integrator(dt=0.005, methods=[langevin], forces=[lj]) integrator.rigid = rigid initial_snapshot = two_particle_snapshot_factory() if initial_snapshot.communicator.rank == 0: initial_snapshot.particles.types = ["A", "B"] sim = simulation_factory(initial_snapshot) sim.seed = 5 sim.operations += integrator sim.run(1)
def test_data_buffers_readonly(local_force_names, two_particle_snapshot_factory, simulation_factory): """Ensure local data buffers for non-custom force classes are read-only.""" nlist = md.nlist.Cell(buffer=0.2) lj = md.pair.LJ(nlist, default_r_cut=2.0) lj.params[('A', 'A')] = dict(epsilon=1.0, sigma=1.0) snap = two_particle_snapshot_factory() sim = simulation_factory(snap) langevin = md.methods.Langevin(hoomd.filter.All(), kT=1) integrator = md.Integrator(dt=0.005, forces=[lj], methods=[langevin]) sim.operations.integrator = integrator sim.run(2) for local_force_name in local_force_names: with getattr(lj, local_force_name) as arrays: if not _gpu_device_and_no_cupy(sim): _assert_buffers_readonly(arrays)
def test_rcut(simulation_factory, two_particle_snapshot_factory): lj = md.pair.LJ(nlist=md.nlist.Cell(), r_cut=2.5) assert lj.r_cut.default == 2.5 lj.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} with pytest.raises(hoomd.data.typeconverter.TypeConversionError): lj.r_cut[('A', 'A')] = 'str' with pytest.raises(hoomd.data.typeconverter.TypeConversionError): lj.r_cut[('A', 'A')] = [1, 2, 3] sim = simulation_factory(two_particle_snapshot_factory(dimensions=3, d=.5)) integrator = md.Integrator(dt=0.005) integrator.forces.append(lj) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator lj.r_cut[('A', 'A')] = 2.5 assert _equivalent_data_structures({('A', 'A'): 2.5}, lj.r_cut.to_dict()) sim.run(0) assert _equivalent_data_structures({('A', 'A'): 2.5}, lj.r_cut.to_dict())
def test_shift_mode_with_lrc(simulation_factory, two_particle_snapshot_factory, mode): cell = md.nlist.Cell(buffer=0.4) lj = md.pair.LJ(nlist=cell, default_r_cut=2.5, mode=mode, tail_correction=True) lj.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} snap = two_particle_snapshot_factory(dimensions=3, d=.5) sim = simulation_factory(snap) integrator = md.Integrator(dt=0.005) integrator.forces.append(lj) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator shift_allowed_modes = {'none'} if mode not in shift_allowed_modes: with pytest.raises(RuntimeError): sim.run(1) else: sim.run(1)
def test_attaching(simulation_factory, two_particle_snapshot_factory, valid_body_definition): rigid = md.constrain.Rigid() rigid.body["A"] = valid_body_definition langevin = md.methods.Langevin(kT=2.0, filter=hoomd.filter.Rigid()) integrator = md.Integrator(dt=0.005, methods=[langevin]) integrator.rigid = rigid initial_snapshot = two_particle_snapshot_factory() if initial_snapshot.communicator.rank == 0: initial_snapshot.particles.types = ["A", "B"] sim = simulation_factory(initial_snapshot) rigid.create_bodies(sim.state) sim.operations += integrator sim.run(0) for key, value in rigid.body["A"].items(): if (isinstance(value, Sequence) and len(value) > 0 and not isinstance(value[0], str)): assert np.allclose(value, valid_body_definition[key]) else: assert value == valid_body_definition[key]
def test_run(simulation_factory, lattice_snapshot_factory, pair_potential): snap = lattice_snapshot_factory(particle_types=['A', 'B'], n=7, a=2.0, r=0.01) if snap.communicator.rank == 0: snap.particles.typeid[:] = np.random.randint(0, len(snap.particles.types), snap.particles.N) sim = simulation_factory(snap) integrator = md.Integrator(dt=0.005, integrate_rotational_dof=True) integrator.forces.append(pair_potential) integrator.methods.append(md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator old_snap = sim.state.get_snapshot() sim.run(5) new_snap = sim.state.get_snapshot() forces = pair_potential.forces energies = pair_potential.energies if new_snap.communicator.rank == 0: assert not np.allclose(new_snap.particles.position, old_snap.particles.position) assert np.any(energies != 0) assert np.any(forces != 0)
def test_force_energy_accuracy(simulation_factory, two_particle_snapshot_factory, forces_and_energies): pot_name = forces_and_energies.pair_potential.__name__ if pot_name == "DPD" or pot_name == "DPDLJ": pytest.skip("Cannot test force energy accuracy for " + pot_name + " pair force") pot = forces_and_energies.pair_potential(**forces_and_energies.extra_args, nlist=md.nlist.Cell(), default_r_cut=2.5) pot.params[('A', 'A')] = forces_and_energies.pair_potential_params snap = two_particle_snapshot_factory(particle_types=['A'], d=0.75) _update_snap(forces_and_energies.pair_potential, snap) sim = simulation_factory(snap) integrator = md.Integrator(dt=0.005) integrator.forces.append(pot) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator sim.run(0) particle_distances = [0.75, 1.5] for i in range(len(particle_distances)): d = particle_distances[i] r = np.array([0, 0, d]) / d snap = sim.state.snapshot if snap.communicator.rank == 0: snap.particles.position[0] = [0, 0, .1] snap.particles.position[1] = [0, 0, d + .1] sim.state.snapshot = snap sim_energies = sim.operations.integrator.forces[0].energies sim_forces = sim.operations.integrator.forces[0].forces if sim_energies is not None: assert isclose(sum(sim_energies), forces_and_energies.energies[i]) assert isclose(sim_forces[0], forces_and_energies.forces[i] * r) assert isclose(sim_forces[0], -forces_and_energies.forces[i] * r)
def test_attached_params(simulation_factory, lattice_snapshot_factory, valid_params): pair_potential, pair_potential_dict, extra_args = valid_params pair_keys = valid_params.pair_potential_params.keys() particle_types = list(set(itertools.chain.from_iterable(pair_keys))) pot = valid_params.pair_potential(**valid_params.extra_args, nlist=md.nlist.Cell(), r_cut=2.5, mode='none') pot.params = valid_params.pair_potential_params snap = lattice_snapshot_factory(particle_types=particle_types, n=10, a=1.5, r=0.01) _update_snap(valid_params.pair_potential, snap) if snap.exists: snap.particles.typeid[:] = np.random.randint(0, len(snap.particles.types), snap.particles.N) sim = simulation_factory(snap) sim.operations.integrator = md.Integrator(dt=0.005) sim.operations.integrator.forces.append(pot) sim.run(1) assert _equivalent_data_structures(valid_params.pair_potential_params, pot.params.to_dict())
def test_tail_corrections(simulation_factory, two_particle_snapshot_factory): # the tail correction should always decrease the potential energy with a LJ # pair potential and the cutoff is greater than sigma # further, the pressure correction should always be negative for the LJ # potenial if r_cut is greater than 2^(1/6)sigma sims = {} sigma = 1.0 epsilon = 0.5 d_pair = 1.5 r_cut = 2.0 for tail_correction in [True, False]: cell = md.nlist.Cell(buffer=0.4) lj = md.pair.LJ(nlist=cell, default_r_cut=r_cut, mode='none', tail_correction=tail_correction) lj.params[('A', 'A')] = {'sigma': sigma, 'epsilon': epsilon} snap = two_particle_snapshot_factory(dimensions=3, d=d_pair) v1 = np.array([0.46168675, -0.21020661, 0.21240303]) v2 = -v1 # zero linear momentum if snap.communicator.rank == 0: snap.particles.velocity[0] = v1 snap.particles.velocity[1] = v2 sim = simulation_factory(snap) integrator = md.Integrator(dt=0.005) integrator.forces.append(lj) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator sim.always_compute_pressure = True thermodynamic_properties = hoomd.md.compute.ThermodynamicQuantities( filter=hoomd.filter.All()) sim.operations.computes.append(thermodynamic_properties) sim.run(0) sims[tail_correction] = sim e_true = sims[True].operations.computes[0].potential_energy e_false = sims[False].operations.computes[0].potential_energy p_true = sims[True].operations.computes[0].pressure p_false = sims[False].operations.computes[0].pressure N = sim.state.N_particles volume = sim.state.box.volume rho = N / volume def lj_energy(r, sig, eps): return 4 * eps * ((sig / r)**12 - (sig / r)**6) def energy_correction(sigma, epsilon, r_cut, rho, N): """Long-range tail correction to energy.""" lj1 = 4.0 * epsilon * sigma**12.0 lj2 = 4.0 * epsilon * sigma**6.0 integral = lj1 / 9 / r_cut**9 - lj2 / 3 / r_cut**3 return 2 * N * np.pi * rho * integral def lj_force_mag(r, sig, eps): """Magnitude of force on particles from LJ potential a distance r.""" return 24 * eps / r * (2 * (sig / r)**12 - (sig / r)**6) def pressure_correction(sigma, epsilon, r_cut, rho): """Long-range tail correction to pressure.""" lj1 = 4.0 * epsilon * sigma**12.0 lj2 = 4.0 * epsilon * sigma**6.0 integral = lj1 * 4 / 3 / r_cut**9 - lj2 * 2 / r_cut**3 return 4 / 6 * rho**2 * np.pi * integral dE = energy_correction(sigma, epsilon, r_cut, rho, N) mass = 1.0 ke = 0.5 * mass * (np.dot(v1, v1) + np.dot(v2, v2)) ljf = lj_force_mag(d_pair, sigma, epsilon) mean_virial = 1 / 3 * ljf * d_pair pressure_should_be = (2 * ke / 3 + mean_virial) / volume dP = pressure_correction(sigma, epsilon, r_cut, rho) # energy regression test np.testing.assert_allclose(e_false, lj_energy(d_pair, sigma, epsilon)) np.testing.assert_allclose(e_true, lj_energy(d_pair, sigma, epsilon) + dE) # pressure regression test np.testing.assert_allclose(p_false, pressure_should_be) np.testing.assert_allclose(p_true, pressure_should_be + dP) # make sure corrections correct in the right direction assert e_true < e_false assert p_true < p_false
def make_langevin_integrator(force): integrator = md.Integrator(dt=0.005, integrate_rotational_dof=True) integrator.forces.append(force) integrator.methods.append(md.methods.Langevin(hoomd.filter.All(), kT=1)) return integrator
def test_conservation(simulation_factory, lattice_snapshot_factory): # For test, use a unit area hexagon. particle_vertices = np.array([[6.20403239e-01, 0.00000000e+00, 0], [3.10201620e-01, 5.37284966e-01, 0], [-3.10201620e-01, 5.37284966e-01, 0], [-6.20403239e-01, 7.59774841e-17, 0], [-3.10201620e-01, -5.37284966e-01, 0], [3.10201620e-01, -5.37284966e-01, 0]]) area = 1.0 circumcircle_radius = 0.6204032392788702 incircle_radius = 0.5372849659264116 num_vertices = len(particle_vertices) circumcircle_diameter = 2 * circumcircle_radius # Just initialize in a simple cubic lattice. sim = simulation_factory( lattice_snapshot_factory(a=4 * circumcircle_diameter, n=10, dimensions=2)) sim.seed = 175 # Initialize moments of inertia since original simulation was HPMC. mass = area # https://math.stackexchange.com/questions/2004798/moment-of-inertia-for-a-n-sided-regular-polygon # noqa moment_inertia = (mass * circumcircle_diameter**2 / 6) moment_inertia *= (1 + 2 * np.cos(np.pi / num_vertices)**2) with sim.state.cpu_local_snapshot as snapshot: snapshot.particles.mass[:] = mass snapshot.particles.moment_inertia[:] = np.array([0, 0, moment_inertia]) # Not sure if this should be incircle or circumcircle; # probably doesn't matter based on current usage, but may # matter in the future for the potential if it's modified # to actually use diameter. snapshot.particles.diameter[:] = circumcircle_diameter kT = 0.3 sim.state.thermalize_particle_momenta(hoomd.filter.All(), kT) # Create box resize updater packing_fraction = 0.4 final_area = area * sim.state.N_particles / packing_fraction L_final = np.sqrt(final_area) final_box = hoomd.Box.square(L_final) n_compression_start = int(1e4) n_compression_end = int(1e5) n_compression_total = n_compression_end - n_compression_start box_resize = hoomd.update.BoxResize( box1=sim.state.box, box2=final_box, trigger=int(n_compression_total / 10000), variant=hoomd.variant.Ramp(0, 1, n_compression_start, n_compression_total), filter=hoomd.filter.All()) sim.operations += box_resize # Define forces and methods r_cut_scale = 1.3 kernel_scale = (1 / np.cos(np.pi / num_vertices)) incircle_diameter = 2 * incircle_radius r_cut_set = incircle_diameter * kernel_scale * r_cut_scale alj = md.pair.aniso.ALJ(default_r_cut=r_cut_set, nlist=md.nlist.Cell(0.4)) alj.shape["A"] = { "vertices": particle_vertices, "faces": [], "rounding_radii": 0 } alpha = 0 # Make it WCA-only (no attraction) eps_att = 1.0 alj.params[("A", "A")] = { "epsilon": eps_att, "sigma_i": incircle_diameter, "sigma_j": incircle_diameter, "alpha": alpha } nve = md.methods.NVE(filter=hoomd.filter.All()) integrator = md.Integrator(dt=1e-4, forces=[alj], methods=[nve], integrate_rotational_dof=True) sim.operations.integrator = integrator # Compress box sim.run(n_compression_end) thermo = md.compute.ThermodynamicQuantities(hoomd.filter.All()) sim.operations += thermo # Reset velocities after the compression, and equilibriate sim.state.thermalize_particle_momenta(hoomd.filter.All(), kT) sim.run(1000) # run sim and get values back w = hoomd.conftest.ManyListWriter([(thermo, 'potential_energy'), (thermo, 'kinetic_energy'), (integrator, 'linear_momentum')]) writer = hoomd.write.CustomWriter(action=w, trigger=1) sim.operations.writers.append(writer) sim.run(1000) pe, ke, momentum = w.data total_energies = np.array(pe) + np.array(ke) # Ensure energy conservation up to the 3 digit per-particle. npt.assert_allclose(total_energies, total_energies[0], atol=0.03 * sim.state.N_particles) # Test momentum conservation. p_magnitude = np.linalg.norm(momentum, axis=-1) npt.assert_allclose(p_magnitude, p_magnitude[0], atol=1e-13)
def test_type_shapes(simulation_factory, two_particle_snapshot_factory): alj = md.pair.aniso.ALJ(md.nlist.Cell(buffer=0.1)) sim = simulation_factory(two_particle_snapshot_factory(d=2.0)) sim.operations.integrator = md.Integrator(0.005, forces=[alj]) alj.r_cut.default = 2.5 octahedron = [(0.5, 0, 0), (-0.5, 0, 0), (0, 0.5, 0), (0, -0.5, 0), (0, 0, 0.5), (0, 0, -0.5)] faces = [[5, 3, 1], [0, 3, 5], [1, 3, 4], [4, 3, 0], [5, 2, 0], [1, 2, 5], [0, 2, 4], [4, 2, 1]] rounding_radius = 0.1 alj.shape["A"] = { "vertices": octahedron, "faces": faces, "rounding_radii": rounding_radius } # We use a non-zero sigma_i to ensure that it is added appropriately to the # rounding radius. alj.params[("A", "A")] = { "epsilon": 1.0, "sigma_i": 0.1, "sigma_j": 0.1, "alpha": 1 } with pytest.raises(hoomd.error.DataAccessError): alj.type_shapes def get_rounding_radius(base, param_spec): modification = param_spec["sigma_i"] * param_spec["contact_ratio_i"] return rounding_radius + modification / 2 sim.run(0) shape_spec = alj.type_shapes assert len(shape_spec) == 1 shape_spec = shape_spec[0] assert shape_spec["type"] == "ConvexPolyhedron" assert np.allclose(shape_spec["vertices"], octahedron) assert np.isclose( shape_spec["rounding_radius"], get_rounding_radius(rounding_radius, alj.params[("A", "A")])) ellipse_axes = (0.1, 0.2, 0.3) alj.shape["A"] = { "vertices": [], "faces": [], "rounding_radii": ellipse_axes } shape_spec = alj.type_shapes assert len(shape_spec) == 1 shape_spec = shape_spec[0] assert shape_spec["type"] == "Ellipsoid" assert np.isclose( shape_spec["a"], get_rounding_radius(ellipse_axes[0], alj.params[("A", "A")])) assert np.isclose( shape_spec["a"], get_rounding_radius(ellipse_axes[1], alj.params[("A", "A")])) assert np.isclose( shape_spec["a"], get_rounding_radius(ellipse_axes[2], alj.params[("A", "A")])) sim.operations.integrator.forces.remove(alj) sim = simulation_factory(two_particle_snapshot_factory(dimensions=2, d=2)) sim.operations.integrator = md.Integrator(0.005, forces=[alj]) square = [(0.5, 0, 0), (-0.5, 0, 0), (-0.5, -0.5, 0), (0.5, 0.5, 0)] alj.shape["A"] = { "vertices": square, "faces": [], "rounding_radii": rounding_radius } sim.run(0) shape_spec = alj.type_shapes assert len(shape_spec) == 1 shape_spec = shape_spec[0] assert shape_spec["type"] == "Polygon" assert np.allclose(shape_spec["vertices"], np.array(square)[:, :2]) assert np.isclose( shape_spec["rounding_radius"], get_rounding_radius(rounding_radius, alj.params[("A", "A")])) alj.shape["A"] = { "vertices": [], "faces": [], "rounding_radii": ellipse_axes } shape_spec = alj.type_shapes assert len(shape_spec) == 1 shape_spec = shape_spec[0] assert shape_spec["type"] == "Ellipsoid" assert np.isclose( shape_spec["a"], get_rounding_radius(ellipse_axes[0], alj.params[("A", "A")])) assert np.isclose( shape_spec["a"], get_rounding_radius(ellipse_axes[1], alj.params[("A", "A")]))
def populate_sim(sim): """Add an integrator for the following tests.""" sim.operations.integrator = md.Integrator( dt=0.005, methods=[md.methods.NVE(hoomd.filter.All())]) return sim
def test_energy_shifting(simulation_factory, two_particle_snapshot_factory): # A subtle bug existed where we used "shifted" instead of "shift" in Python # and in C++ we used else if clauses with no error raised if the set Python # mode fell through. This means the actual shift mode was not set. pytest.skip("Test is broken.") def S_r(r, r_cut, r_on): if r < r_on: return 1 elif r > r_cut: return 0 numerator = ( (r_cut**2 - r**2)**2) * (r_cut**2 + 2 * r**2 - 3 * r_on**2) denominator = (r_cut**2 - r_on**2)**3 return numerator / denominator r_cut = 2.5 r_on = 0.5 r = 1.0 lj = md.pair.LJ(nlist=md.nlist.Cell(), r_cut=r_cut) lj.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} sim = simulation_factory(two_particle_snapshot_factory(dimensions=3, d=r)) integrator = md.Integrator(dt=0.005) integrator.forces.append(lj) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator sim.run(0) energies = sim.operations.integrator.forces[0].energies if energies is not None: E_r = sum(energies) snap = sim.state.snapshot if snap.exists: snap.particles.position[0] = [0, 0, .1] snap.particles.position[1] = [0, 0, r_cut + .1] sim.state.snapshot = snap energies = sim.operations.integrator.forces[0].energies if energies is not None: E_rcut = sum(energies) lj_shift = md.pair.LJ(nlist=md.nlist.Cell(), mode='shift', r_cut=r_cut) lj_shift.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} integrator = md.Integrator(dt=0.005) integrator.forces.append(lj_shift) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator sim.run(0) snap = sim.state.snapshot if snap.exists: snap.particles.position[0] = [0, 0, .1] snap.particles.position[1] = [0, 0, r + .1] sim.state.snapshot = snap energies = sim.operations.integrator.forces[0].energies if energies is not None: assert sum(energies) == E_r - E_rcut lj_xplor = md.pair.LJ(nlist=md.nlist.Cell(), mode='xplor', r_cut=r_cut) lj_xplor.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} lj_xplor.r_on[('A', 'A')] = 0.5 integrator = md.Integrator(dt=0.005) integrator.forces.append(lj_xplor) integrator.methods.append( hoomd.md.methods.Langevin(hoomd.filter.All(), kT=1)) sim.operations.integrator = integrator sim.run(0) energies = sim.operations.integrator.forces[0].energies if energies is not None: xplor_E = sum(energies) assert xplor_E == E_r * S_r(r, r_cut, r_on) lj_xplor.r_on[('A', 'A')] = 3.0 assert sum(energies) == E_r - E_rcut
def test_energy_shifting(simulation_factory, two_particle_snapshot_factory): # A subtle bug existed where we used "shifted" instead of "shift" in Python # and in C++ we used else if clauses with no error raised if the set Python # mode fell through. This means the actual shift mode was not set. def S_r(r, r_cut, r_on): # noqa: N802 - allow uppercase function name if r < r_on: return 1 elif r > r_cut: return 0 numerator = ( (r_cut**2 - r**2)**2) * (r_cut**2 + 2 * r**2 - 3 * r_on**2) denominator = (r_cut**2 - r_on**2)**3 return numerator / denominator def set_distance(simulation, distance): snap = sim.state.get_snapshot() if snap.communicator.rank == 0: snap.particles.position[0] = [0, 0, .1] snap.particles.position[1] = [0, 0, distance + .1] sim.state.set_snapshot(snap) def get_energy(simulation): energies = sim.operations.integrator.forces[0].energies if energies is None: return return sum(energies) r_cut = 2.5 distance = 1.1 lj = md.pair.LJ(nlist=md.nlist.Cell(buffer=0.4), default_r_cut=r_cut * 1.2) lj.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} sim = simulation_factory( two_particle_snapshot_factory(dimensions=3, d=distance)) integrator = md.Integrator(dt=0.005, forces=[lj]) sim.operations.integrator = integrator sim.run(0) E_r = get_energy(sim) # Get energy at r_cut. set_distance(sim, r_cut) E_r_cut = get_energy(sim) lj_shift = md.pair.LJ(nlist=md.nlist.Cell(buffer=0.4), mode='shift', default_r_cut=r_cut) lj_shift.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} integrator.forces = [lj_shift] set_distance(sim, distance) energies = sim.operations.integrator.forces[0].energies if energies is not None: assert math.isclose(sum(energies), E_r - E_r_cut) r_on = 0.5 lj_xplor = md.pair.LJ(nlist=md.nlist.Cell(buffer=0.4), mode='xplor', default_r_cut=r_cut) lj_xplor.params[('A', 'A')] = {'sigma': 1, 'epsilon': 0.5} lj_xplor.r_on[('A', 'A')] = r_on integrator.forces = [lj_xplor] # When 0 < r_on < r_ij < r_cut energies = sim.operations.integrator.forces[0].energies if energies is not None: assert math.isclose(sum(energies), E_r * S_r(distance, r_cut, r_on)) # When 0 < r_ij < r_on < r_cut lj_xplor.r_on[('A', 'A')] = distance * 1.2 sim.run(1) # recompute forces energies = sim.operations.integrator.forces[0].energies if energies is not None: assert math.isclose(sum(energies), E_r) # When 0 < r_ij < r_cut < r_on lj_xplor.r_on[('A', 'A')] = r_cut * 1.2 sim.run(1) # recompute forces energies = sim.operations.integrator.forces[0].energies if energies is not None: assert math.isclose(sum(energies), E_r - E_r_cut)
def make_langevin_integrator(force): integrator = md.Integrator(dt=0.005) integrator.forces.append(force) integrator.methods.append( md.methods.Langevin(hoomd.filter.All(), kT=1, seed=1)) return integrator