Beispiel #1
0
    def test_lj(self):

        lj_eps = 1.92
        lj_sig = 1.03
        lj_cut = 1.123
        lj_shift = 0.92

        self.system.non_bonded_inter[0, 0].lennard_jones.set_params(
            epsilon=lj_eps, sigma=lj_sig, cutoff=lj_cut, shift=lj_shift)

        for i in range(113):
            self.system.part[1].pos = self.system.part[1].pos + self.step
            self.system.integrator.run(recalc_forces=True, steps=0)

            # Calculate energies
            E_sim = self.system.analysis.energy()["non_bonded"]
            E_ref = tests_common.lj_potential(
                (i + 1) * self.step_width, lj_eps, lj_sig, lj_cut,
                shift=lj_shift)

            # Calculate forces
            f0_sim = self.system.part[0].f
            f1_sim = self.system.part[1].f
            f1_ref = self.axis * \
                tests_common.lj_force(espressomd, r=(i + 1) * self.step_width,
                                      eps=lj_eps, sig=lj_sig, cutoff=lj_cut)

            # Check that energies match, ...
            self.assertFractionAlmostEqual(E_sim, E_ref)
            # force equals minus the counter-force  ...
            self.assertTrue((f0_sim == -f1_sim).all())
            # and has correct value.
            self.assertItemsFractionAlmostEqual(f1_sim, f1_ref)

        self.system.non_bonded_inter[0, 0].lennard_jones.set_params(epsilon=0.)
def lj_cos2_potential(r, epsilon, sigma, offset, width):
    V = 0.
    r_min = offset + np.power(2., 1. / 6.) * sigma
    r_cut = r_min + width
    if r < r_min:
        V = tests_common.lj_potential(r, epsilon=epsilon, sigma=sigma,
                                      offset=offset, cutoff=r_cut, shift=0.)
    elif r < r_cut:
        V = -epsilon * np.power(np.cos(np.pi /
                                       (2. * width) * (r - r_min)), 2)
    return V
def lj_cos_potential(r, epsilon, sigma, cutoff, offset):
    V = 0.
    r_min = offset + np.power(2., 1. / 6.) * sigma
    r_cut = cutoff + offset
    if r < r_min:
        V = tests_common.lj_potential(r, epsilon=epsilon, sigma=sigma,
                                      cutoff=cutoff, offset=offset, shift=0.)
    elif r < r_cut:
        alpha = np.pi / \
            (np.power(r_cut - offset, 2) - np.power(r_min - offset, 2))
        beta = np.pi - np.power(r_min - offset, 2) * alpha
        V = 0.5 * epsilon * \
            (np.cos(alpha * np.power(r - offset, 2) + beta) - 1.)
    return V
Beispiel #4
0
class WidomInsertionTest(ut.TestCase):
    """Test the implementation of the widom insertion.

       The excess chemical potential is calculated for identical particles in
       a 20 cubed box with a single particle, interacting via a LJ-potential
       (cut-off at 5 sigma)."""

    N0 = 1
    TEMPERATURE = 0.5
    TYPE_HA = 0
    CHARGE_HA = 0
    LJ_EPS = 1.0
    LJ_SIG = 1.0
    LJ_CUT = 5
    BOX_L = 2 * LJ_CUT
    LJ_SHIFT = lj_potential(LJ_CUT, LJ_EPS, LJ_SIG, LJ_CUT + 1, 0.0)

    radius = np.linspace(1e-10, LJ_CUT, 1000)
    # numerical integration for radii smaller than the cut-off in spherical
    # coordinates
    integrateUpToCutOff = 4 * np.pi * np.trapz(radius**2 * np.exp(
        -lj_potential(radius, LJ_EPS, LJ_SIG, LJ_CUT, LJ_SHIFT) / TEMPERATURE),
                                               x=radius)
    # numerical solution for V_lj=0 => corresponds to the volume (as exp(0)=1)
    integreateRest = (BOX_L**3 - 4.0 / 3.0 * np.pi * LJ_CUT**3)

    # calculate excess chemical potential of the system, see Frenkel Smith,
    # p 174. Note: He uses scaled coordinates, which is why we need to divide
    # by the box volume
    target_mu_ex = -TEMPERATURE * \
        np.log((integrateUpToCutOff + integreateRest) / BOX_L**3)

    system = espressomd.System(box_l=np.ones(3) * BOX_L)
    system.cell_system.set_n_square()
    system.seed = system.cell_system.get_state()['n_nodes'] * [2]
    np.random.seed(69)  # make reaction code fully deterministic
    system.cell_system.skin = 0.4
    volume = np.prod(system.box_l)  # cuboid box

    Widom = reaction_ensemble.WidomInsertion(temperature=TEMPERATURE, seed=1)

    def setUp(self):
        self.system.part.add(id=0,
                             pos=0.5 * self.system.box_l,
                             type=self.TYPE_HA)

        self.system.non_bonded_inter[self.TYPE_HA,
                                     self.TYPE_HA].lennard_jones.set_params(
                                         epsilon=self.LJ_EPS,
                                         sigma=self.LJ_SIG,
                                         cutoff=self.LJ_CUT,
                                         shift="auto")

        self.Widom.add_reaction(reactant_types=[],
                                reactant_coefficients=[],
                                product_types=[self.TYPE_HA],
                                product_coefficients=[1],
                                default_charges={self.TYPE_HA: self.CHARGE_HA})

    def test_widom_insertion(self):
        system = WidomInsertionTest.system
        Widom = WidomInsertionTest.Widom
        target_mu_ex = WidomInsertionTest.target_mu_ex

        system.seed = system.cell_system.get_state()['n_nodes'] * [
            np.random.randint(5)
        ]
        num_samples = 100000
        for _ in range(num_samples):
            # 0 for insertion reaction
            Widom.measure_excess_chemical_potential(0)
        mu_ex = Widom.measure_excess_chemical_potential(0)
        deviation_mu_ex = abs(mu_ex[0] - target_mu_ex)

        # error
        self.assertLess(
            deviation_mu_ex - 1e-3,
            0.0,
            msg=
            "\nExcess chemical potential for single LJ-particle computed via widom insertion gives a wrong value.\n"
            + "  average mu_ex: " + str(mu_ex[0]) + "   mu_ex_std_err: " +
            str(mu_ex[1]) + "  target_mu_ex: " + str(target_mu_ex))
class WidomInsertionTest(ut.TestCase):

    """Test the implementation of the widom insertion.

       The excess chemical potential is calculated for identical particles in
       a 20 cubed box with a single particle, interacting via a LJ-potential
       (cut-off at 5 sigma)."""

    N0 = 1
    TEMPERATURE = 0.5
    TYPE_HA = 0
    CHARGE_HA = 0
    LJ_EPS = 1.0
    LJ_SIG = 1.0
    LJ_CUT = 5
    BOX_L = 2 * LJ_CUT
    LJ_SHIFT = tests_common.lj_potential(
        LJ_CUT, LJ_EPS, LJ_SIG, LJ_CUT + 1.0, 0.0)

    radius = np.linspace(1e-10, LJ_CUT, 1000)
    # numerical integration for radii smaller than the cut-off in spherical
    # coordinates
    integrateUpToCutOff = 4 * np.pi * np.trapz(
        radius**2 * np.exp(-tests_common.lj_potential(radius,
                                                      LJ_EPS,
                                                      LJ_SIG,
                                                      LJ_CUT,
                                                      LJ_SHIFT) / TEMPERATURE),
        x=radius)
    # numerical solution for V_lj=0 => corresponds to the volume (as exp(0)=1)
    integreateRest = (BOX_L**3 - 4.0 / 3.0 * np.pi * LJ_CUT**3)

    # calculate excess chemical potential of the system, see Frenkel Smith,
    # p 174. Note: He uses scaled coordinates, which is why we need to divide
    # by the box volume
    target_mu_ex = -TEMPERATURE * \
        np.log((integrateUpToCutOff + integreateRest) / BOX_L**3)

    system = espressomd.System(box_l=np.ones(3) * BOX_L)
    system.cell_system.set_n_square()
    np.random.seed(69)  # make reaction code fully deterministic
    system.cell_system.skin = 0.4

    Widom = espressomd.reaction_ensemble.WidomInsertion(
        kT=TEMPERATURE, seed=1)

    # Set the hidden particle type to the lowest possible number to speed
    # up the simulation
    Widom.set_non_interacting_type(type=1)

    def setUp(self):
        self.system.part.add(pos=0.5 * self.system.box_l, type=self.TYPE_HA)

        self.system.non_bonded_inter[self.TYPE_HA, self.TYPE_HA].lennard_jones.set_params(
            epsilon=self.LJ_EPS, sigma=self.LJ_SIG, cutoff=self.LJ_CUT,
            shift="auto")

        self.Widom.add_reaction(
            reactant_types=[],
            reactant_coefficients=[],
            product_types=[self.TYPE_HA],
            product_coefficients=[1],
            default_charges={self.TYPE_HA: self.CHARGE_HA})

    def test_widom_insertion(self):

        num_samples = 10000
        particle_insertion_potential_energy_samples = []

        for _ in range(num_samples):
            # 0 for insertion reaction
            particle_insertion_potential_energy = self.Widom.calculate_particle_insertion_potential_energy(
                reaction_id=0)
            particle_insertion_potential_energy_samples.append(
                particle_insertion_potential_energy)

        mu_ex_mean, mu_ex_Delta = self.Widom.calculate_excess_chemical_potential(
            particle_insertion_potential_energy_samples=particle_insertion_potential_energy_samples)

        deviation_mu_ex = abs(np.mean(mu_ex_mean) - self.target_mu_ex)

        self.assertLess(
            deviation_mu_ex,
            1e-3,
            msg="\nExcess chemical potential for single LJ-particle computed via Widom insertion is wrong.\n"
            + f"  average mu_ex: {np.mean(mu_ex_mean):.4f}"
            + f"   mu_ex_std_err: {np.std(mu_ex_Delta):.5f}"
            + f"  target_mu_ex: {self.target_mu_ex:.4f}"
        )