Пример #1
0
def test_am1ccc():

    patterns = [
        ["[#6X4:1]-[#1:2]", 0.46323257920556493],
        ["[#6X3$(*=[#8,#16]):1]-[#6a:2]", 0.24281402370571598],
        ["[#6X3$(*=[#8,#16]):1]-[#8X1,#8X2:2]", 1.0620166764992722],
        [
            "[#6X3$(*=[#8,#16]):1]=[#8X1$(*=[#6X3]-[#8X2]):2]",
            2.227759732057297
        ],
        ["[#6X3$(*=[#8,#16]):1]=[#8X1,#8X2:2]", 2.8182928673804217],
        ["[#6a:1]-[#8X1,#8X2:2]", 0.5315976926761063],
        ["[#6a:1]-[#1:2]", 0.0],
        ["[#6a:1]:[#6a:2]", 0.0],
        ["[#6a:1]:[#6a:2]", 0.0],
        ["[#8X1,#8X2:1]-[#1:2]", -2.3692047944101415],
        ["[#16:1]-[#8:2]", 99.0],
    ]

    smirks = [x[0] for x in patterns]
    params = np.array([x[1] * np.sqrt(138.935456) for x in patterns])
    props = None

    am1h = nonbonded.AM1CCCHandler(smirks, params, props)
    obj = am1h.serialize()
    all_handlers = deserialize_handlers(bin_to_str(obj))

    assert len(all_handlers) == 1

    new_am1h = all_handlers[0]
    np.testing.assert_equal(new_am1h.smirks, am1h.smirks)
    np.testing.assert_equal(new_am1h.params, am1h.params)
    assert new_am1h.props == am1h.props
Пример #2
0
    def load_from_file(cls, path: Union[str, Path]) -> "Forcefield":
        """Load a forcefield from a path

        Parameters
        ----------

        path: string or pathlib.Path
            Either the filename of a built in ff (smirnoff_1_1_0_sc.py) or a path to a new forcefield file

        Returns
        -------
        Forcefield
            Return a ForceField object constructed from parameters file
        """
        original_path = str(path)
        path = Path(path)  # Safe to construct a Path object from another Path object
        built_in_path = PARAMS_DIR / original_path
        if built_in_path.is_file():
            if path.is_file():
                warn(f"Provided path {original_path} shares name with builtin forcefield, falling back to builtin")
            # Search built in params for the forcefields
            path = built_in_path
        if not path.is_file():
            raise ValueError(f"Unable to find {original_path} in file system or built in forcefields")
        with open(path, "r") as ifs:
            handlers = deserialize_handlers(ifs.read())
        return cls(handlers)
Пример #3
0
def test_proper_torsion():

    # proper torsions have a variadic number of terms

    patterns = [
        ["[*:1]-[#6X3:2]=[#6X3:3]-[*:4]", [[99.0, 99.0, 99.0]]],
        ["[*:1]-[#6X3:2]=[#6X3:3]-[#35:4]", [[99.0, 99.0, 99.0]]],
        [
            "[#9:1]-[#6X3:2]=[#6X3:3]-[#35:4]",
            [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
        ],
        [
            "[#35:1]-[#6X3:2]=[#6X3:3]-[#35:4]",
            [[7.0, 8.0, 9.0], [1.0, 3.0, 5.0], [4.0, 4.0, 4.0]]
        ],
        ["[#9:1]-[#6X3:2]=[#6X3:3]-[#9:4]", [[7.0, 8.0, 9.0]]],
    ]

    smirks = [x[0] for x in patterns]
    params = [x[1] for x in patterns]

    ph = bonded.ProperTorsionHandler(smirks, params, None)
    obj = ph.serialize()
    all_handlers = deserialize_handlers(bin_to_str(obj))

    assert len(all_handlers) == 1

    new_ph = all_handlers[0]
    np.testing.assert_equal(new_ph.smirks, ph.smirks)
    np.testing.assert_equal(new_ph.params, ph.params)
    assert new_ph.props == ph.props
Пример #4
0
def test_parameterize_and_draw_interactions():
    """
    This isn't really tested, but is here to verify that drawing code at least runs.
    """

    ff_handlers = deserialize_handlers(
        open("timemachine/ff/params/smirnoff_1_1_0_sc.py").read())
    ff = Forcefield(ff_handlers)

    mol = Chem.MolFromSmiles("CC(=O)OC1=CC=CC=C1C(=O)O")
    core = [3, 4, 5, 6, 7, 8, 9, 10]

    _draw_impl(mol, core, ff, "aspirin")

    mol = Chem.MolFromSmiles("C(C1=CC=CC=C1)C1=CC=CC=C1")
    core = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

    _draw_impl(mol, core, ff, "met_biphenyl")

    mol = Chem.MolFromSmiles("F[C@](Cl)(Br)C1=CC=CC=C1")
    core = [1, 4, 5, 6, 7, 8, 9]

    _draw_impl(mol, core, ff, "toluene")

    mol = Chem.MolFromSmiles("C1CC2=CC=CC=C12")
    core = [2, 3, 4, 5, 6, 7]

    _draw_impl(mol, core, ff, "ring_open")
Пример #5
0
def test_am1bcc():

    smirks = []
    params = []
    props = None

    am1 = nonbonded.AM1BCCHandler(smirks, params, props)
    obj = am1.serialize()
    all_handlers = deserialize_handlers(bin_to_str(obj))

    am1 = all_handlers[0]
    np.testing.assert_equal(am1.smirks, am1.smirks)
    np.testing.assert_equal(am1.params, am1.params)
    assert am1.props == am1.props
Пример #6
0
def test_am1_differences():

    ff_raw = open("timemachine/ff/params/smirnoff_1_1_0_ccc.py").read()
    ff_handlers = deserialize_handlers(ff_raw)
    for ccc in ff_handlers:
        if isinstance(ccc, nonbonded.AM1CCCHandler):
            break

    suppl = Chem.SDMolSupplier("tests/data/ligands_40.sdf", removeHs=False)
    smi = "[H]c1c(OP(=S)(OC([H])([H])C([H])([H])[H])OC([H])([H])C([H])([H])[H])nc(C([H])(C([H])([H])[H])C([H])([H])[H])nc1C([H])([H])[H]"
    smi = "Clc1c(Cl)c(Cl)c(-c2c(Cl)c(Cl)c(Cl)c(Cl)c2Cl)c(Cl)c1Cl"
    mol = Chem.MolFromSmiles(smi)
    mol = Chem.AddHs(mol)
    mol.SetProp("_Name", "Debug")
    assert AllChem.EmbedMolecule(mol) == 0

    suppl = [mol]
    am1 = nonbonded.AM1Handler([], [], None)
    bcc = nonbonded.AM1BCCHandler([], [], None)

    for mol in suppl:

        am1_params = am1.parameterize(mol)
        ccc_params = ccc.parameterize(mol)
        bcc_params = bcc.parameterize(mol)

        if np.sum(np.abs(ccc_params - bcc_params)) > 0.1:

            print(mol.GetProp("_Name"), Chem.MolToSmiles(mol))
            print("  AM1    CCC    BCC  S ?")
            for atom_idx, atom in enumerate(mol.GetAtoms()):
                a = am1_params[atom_idx]
                b = bcc_params[atom_idx]
                c = ccc_params[atom_idx]
                print("{:6.2f}".format(a),
                      "{:6.2f}".format(c),
                      "{:6.2f}".format(b),
                      atom.GetSymbol(),
                      end="")
                if np.abs(b - c) > 0.1:
                    print(" *")
                else:
                    print(" ")

            assert 0
Пример #7
0
def test_gbsa_handler():

    patterns = [
        ["[*:1]", 99.0, 99.0],
        ["[#1:1]", 99.0, 99.0],
        ["[#1:1]~[#7]", 99.0, 99.0],
        ["[#6:1]", 0.1, 0.2],
        ["[#7:1]", 0.3, 0.4],
        ["[#8:1]", 0.5, 0.6],
        ["[#9:1]", 0.7, 0.8],
        ["[#14:1]", 99.0, 99.0],
        ["[#15:1]", 99.0, 99.0],
        ["[#16:1]", 99.0, 99.0],
        ["[#17:1]", 99.0, 99.0],
    ]

    props = {
        "solvent_dielectric": 78.3,  # matches OBC2,
        "solute_dielectric": 1.0,
        "probe_radius": 0.14,
        "surface_tension": 28.3919551,
        "dielectric_offset": 0.009,
        # GBOBC1
        "alpha": 0.8,
        "beta": 0.0,
        "gamma": 2.909125,
    }

    smirks = [x[0] for x in patterns]
    params = np.array([[x[1], x[2]] for x in patterns])

    gbh = nonbonded.GBSAHandler(smirks, params, props)

    obj = gbh.serialize()
    all_handlers = deserialize_handlers(bin_to_str(obj))

    assert len(all_handlers) == 1

    new_gbh = all_handlers[0]
    np.testing.assert_equal(new_gbh.smirks, gbh.smirks)
    np.testing.assert_equal(new_gbh.params, gbh.params)
    assert new_gbh.props == gbh.props
Пример #8
0
def test_improper_torsion():

    patterns = [
        [
            "[*:1]~[#6X3:2](~[*:3])~[*:4]", 1.5341333333333333,
            3.141592653589793, 2.0
        ],
        ["[*:1]~[#6X3:2](~[#8X1:3])~[#8:4]", 99.0, 99.0, 99.0],
        [
            "[*:1]~[#7X3$(*~[#15,#16](!-[*])):2](~[*:3])~[*:4]", 99.0, 99.0,
            99.0
        ],
        [
            "[*:1]~[#7X3$(*~[#6X3]):2](~[*:3])~[*:4]", 1.3946666666666667,
            3.141592653589793, 2.0
        ],
        ["[*:1]~[#7X3$(*~[#7X2]):2](~[*:3])~[*:4]", 99.0, 99.0, 99.0],
        [
            "[*:1]~[#7X3$(*@1-[*]=,:[*][*]=,:[*]@1):2](~[*:3])~[*:4]", 99.0,
            99.0, 99.0
        ],
        ["[*:1]~[#6X3:2](=[#7X2,#7X3+1:3])~[#7:4]", 99.0, 99.0, 99.0],
    ]

    smirks = [x[0] for x in patterns]
    params = np.array([[x[1], x[2], x[3]] for x in patterns])
    imph = bonded.ImproperTorsionHandler(smirks, params, None)

    obj = imph.serialize()
    all_handlers = deserialize_handlers(bin_to_str(obj))

    assert len(all_handlers) == 1

    new_imph = all_handlers[0]
    np.testing.assert_equal(new_imph.smirks, imph.smirks)
    np.testing.assert_equal(new_imph.params, imph.params)
    assert new_imph.props == imph.props
Пример #9
0
    for address in worker_address_list:
        print("connecting to", address)
        channel = grpc.insecure_channel(
            address,
            options=[
                ("grpc.max_send_message_length", 500 * 1024 * 1024),
                ("grpc.max_receive_message_length", 500 * 1024 * 1024),
            ],
        )

        stub = service_pb2_grpc.WorkerStub(channel)
        stubs.append(stub)

    ff_raw = open(forcefield, "r").read()

    ff_handlers = deserialize_handlers(ff_raw)

    box_width = 3.0
    host_system, host_coords, box, _ = builders.build_water_system(box_width)

    lambda_schedule = np.array(
        [float(x) for x in general_cfg["lambda_schedule"].split(",")])

    num_steps = int(general_cfg["n_steps"])

    for epoch in range(100):

        print("Starting Epoch", epoch,
              datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S"))

        epoch_dir = os.path.join(general_cfg["out_dir"], "epoch_" + str(epoch))
Пример #10
0
def test_serialization_of_ffs():
    for path in glob("timemachine/ff/params/smirnoff_*.py"):
        ff = Forcefield(deserialize_handlers(open(path).read()))
        for handle in ff.get_ordered_handles():
            assert handle is not None, f"{path} failed to deserialize correctly"
Пример #11
0
def test_lennard_jones_handler():

    patterns = [
        ["[#1:1]", 99.0, 999.0],
        ["[#1:1]-[#6X4]", 99.0, 999.0],
        ["[#1:1]-[#6X4]-[#7,#8,#9,#16,#17,#35]", 99.0, 999.0],
        [
            "[#1:1]-[#6X4](-[#7,#8,#9,#16,#17,#35])-[#7,#8,#9,#16,#17,#35]",
            99.0, 999.0
        ],
        [
            "[#1:1]-[#6X4](-[#7,#8,#9,#16,#17,#35])(-[#7,#8,#9,#16,#17,#35])-[#7,#8,#9,#16,#17,#35]",
            99.0, 999.0
        ],
        ["[#1:1]-[#6X4]~[*+1,*+2]", 99.0, 999.0],
        ["[#1:1]-[#6X3]", 99.0, 999.0],
        ["[#1:1]-[#6X3]~[#7,#8,#9,#16,#17,#35]", 99.0, 999.0],
        [
            "[#1:1]-[#6X3](~[#7,#8,#9,#16,#17,#35])~[#7,#8,#9,#16,#17,#35]",
            99.0, 999.0
        ],
        ["[#1:1]-[#6X2]", 99.0, 999.0],
        ["[#1:1]-[#7]", 99.0, 999.0],
        ["[#1:1]-[#8]", 99.0, 999.0],
        ["[#1:1]-[#16]", 99.0, 999.0],
        ["[#6:1]", 0.7, 0.8],
        ["[#6X2:1]", 99.0, 999.0],
        ["[#6X4:1]", 0.1, 0.2],
        ["[#8:1]", 99.0, 999.0],
        ["[#8X2H0+0:1]", 0.5, 0.6],
        ["[#8X2H1+0:1]", 99.0, 999.0],
        ["[#7:1]", 0.3, 0.4],
        ["[#16:1]", 99.0, 999.0],
        ["[#15:1]", 99.0, 999.0],
        ["[#9:1]", 1.0, 1.1],
        ["[#17:1]", 99.0, 999.0],
        ["[#35:1]", 99.0, 999.0],
        ["[#53:1]", 99.0, 999.0],
        ["[#3+1:1]", 99.0, 999.0],
        ["[#11+1:1]", 99.0, 999.0],
        ["[#19+1:1]", 99.0, 999.0],
        ["[#37+1:1]", 99.0, 999.0],
        ["[#55+1:1]", 99.0, 999.0],
        ["[#9X0-1:1]", 99.0, 999.0],
        ["[#17X0-1:1]", 99.0, 999.0],
        ["[#35X0-1:1]", 99.0, 999.0],
        ["[#53X0-1:1]", 99.0, 999.0],
    ]

    smirks = [x[0] for x in patterns]
    params = np.array([[x[1], x[2]] for x in patterns])
    props = None

    ljh = nonbonded.LennardJonesHandler(smirks, params, props)
    obj = ljh.serialize()
    all_handlers = deserialize_handlers(bin_to_str(obj))

    ljh = all_handlers[0]
    np.testing.assert_equal(ljh.smirks, ljh.smirks)
    np.testing.assert_equal(ljh.params, ljh.params)
    assert ljh.props == ljh.props
Пример #12
0
def test_simple_charge_handler():

    patterns = [
        ["[#1:1]", 99.0],
        ["[#1:1]-[#6X4]", 99.0],
        ["[#1:1]-[#6X4]-[#7,#8,#9,#16,#17,#35]", 99.0],
        [
            "[#1:1]-[#6X4](-[#7,#8,#9,#16,#17,#35])-[#7,#8,#9,#16,#17,#35]",
            99.0
        ],
        [
            "[#1:1]-[#6X4](-[#7,#8,#9,#16,#17,#35])(-[#7,#8,#9,#16,#17,#35])-[#7,#8,#9,#16,#17,#35]",
            99.0
        ],
        ["[#1:1]-[#6X4]~[*+1,*+2]", 99.0],
        ["[#1:1]-[#6X3]", 99.0],
        ["[#1:1]-[#6X3]~[#7,#8,#9,#16,#17,#35]", 99.0],
        [
            "[#1:1]-[#6X3](~[#7,#8,#9,#16,#17,#35])~[#7,#8,#9,#16,#17,#35]",
            99.0
        ],
        ["[#1:1]-[#6X2]", 99.0],
        ["[#1:1]-[#7]", 99.0],
        ["[#1:1]-[#8]", 99.0],
        ["[#1:1]-[#16]", 99.0],
        ["[#6:1]", 0.7],
        ["[#6X2:1]", 99.0],
        ["[#6X4:1]", 0.1],
        ["[#8:1]", 99.0],
        ["[#8X2H0+0:1]", 0.5],
        ["[#8X2H1+0:1]", 99.0],
        ["[#7:1]", 0.3],
        ["[#16:1]", 99.0],
        ["[#15:1]", 99.0],
        ["[#9:1]", 1.0],
        ["[#17:1]", 99.0],
        ["[#35:1]", 99.0],
        ["[#53:1]", 99.0],
        ["[#3+1:1]", 99.0],
        ["[#11+1:1]", 99.0],
        ["[#19+1:1]", 99.0],
        ["[#37+1:1]", 99.0],
        ["[#55+1:1]", 99.0],
        ["[#9X0-1:1]", 99.0],
        ["[#17X0-1:1]", 99.0],
        ["[#35X0-1:1]", 99.0],
        ["[#53X0-1:1]", 99.0],
    ]

    smirks = [x[0] for x in patterns]
    params = np.array([x[1] for x in patterns])
    props = None

    sch = nonbonded.SimpleChargeHandler(smirks, params, props)
    obj = sch.serialize()
    all_handlers = deserialize_handlers(bin_to_str(obj))

    assert len(all_handlers) == 1

    new_sch = all_handlers[0]
    np.testing.assert_equal(new_sch.smirks, sch.smirks)
    np.testing.assert_equal(new_sch.params, sch.params)
    assert new_sch.props == sch.props
Пример #13
0
def test_harmonic_bond():

    patterns = [
        ["[#6X4:1]-[#6X4:2]", 0.1, 0.2],
        ["[#6X4:1]-[#6X3:2]", 99.0, 99.0],
        ["[#6X4:1]-[#6X3:2]=[#8X1+0]", 99.0, 99.0],
        ["[#6X3:1]-[#6X3:2]", 99.0, 99.0],
        ["[#6X3:1]:[#6X3:2]", 99.0, 99.0],
        ["[#6X3:1]=[#6X3:2]", 99.0, 99.0],
        ["[#6:1]-[#7:2]", 0.1, 0.2],
        ["[#6X3:1]-[#7X3:2]", 99.0, 99.0],
        ["[#6X4:1]-[#7X3:2]-[#6X3]=[#8X1+0]", 99.0, 99.0],
        ["[#6X3:1](=[#8X1+0])-[#7X3:2]", 99.0, 99.0],
        ["[#6X3:1]-[#7X2:2]", 99.0, 99.0],
        ["[#6X3:1]:[#7X2,#7X3+1:2]", 99.0, 99.0],
        ["[#6X3:1]=[#7X2,#7X3+1:2]", 99.0, 99.0],
        ["[#6:1]-[#8:2]", 99.0, 99.0],
        ["[#6X3:1]-[#8X1-1:2]", 99.0, 99.0],
        ["[#6X4:1]-[#8X2H0:2]", 0.3, 0.4],
        ["[#6X3:1]-[#8X2:2]", 99.0, 99.0],
        ["[#6X3:1]-[#8X2H1:2]", 99.0, 99.0],
        ["[#6X3a:1]-[#8X2H0:2]", 99.0, 99.0],
        ["[#6X3:1](=[#8X1])-[#8X2H0:2]", 99.0, 99.0],
        ["[#6:1]=[#8X1+0,#8X2+1:2]", 99.0, 99.0],
        ["[#6X3:1](~[#8X1])~[#8X1:2]", 99.0, 99.0],
        ["[#6X3:1]~[#8X2+1:2]~[#6X3]", 99.0, 99.0],
        ["[#6X2:1]-[#6:2]", 99.0, 99.0],
        ["[#6X2:1]-[#6X4:2]", 99.0, 99.0],
        ["[#6X2:1]=[#6X3:2]", 99.0, 99.0],
        ["[#6:1]#[#7:2]", 99.0, 99.0],
        ["[#6X2:1]#[#6X2:2]", 99.0, 99.0],
        ["[#6X2:1]-[#8X2:2]", 99.0, 99.0],
        ["[#6X2:1]-[#7:2]", 99.0, 99.0],
        ["[#6X2:1]=[#7:2]", 99.0, 99.0],
        ["[#16:1]=[#6:2]", 99.0, 99.0],
        ["[#6X2:1]=[#16:2]", 99.0, 99.0],
        ["[#7:1]-[#7:2]", 99.0, 99.0],
        ["[#7X3:1]-[#7X2:2]", 99.0, 99.0],
        ["[#7X2:1]-[#7X2:2]", 99.0, 99.0],
        ["[#7:1]:[#7:2]", 99.0, 99.0],
        ["[#7:1]=[#7:2]", 99.0, 99.0],
        ["[#7+1:1]=[#7-1:2]", 99.0, 99.0],
        ["[#7:1]#[#7:2]", 99.0, 99.0],
        ["[#7:1]-[#8X2:2]", 99.0, 99.0],
        ["[#7:1]~[#8X1:2]", 99.0, 99.0],
        ["[#8X2:1]-[#8X2:2]", 99.0, 99.0],
        ["[#16:1]-[#6:2]", 99.0, 99.0],
        ["[#16:1]-[#1:2]", 99.0, 99.0],
        ["[#16:1]-[#16:2]", 99.0, 99.0],
        ["[#16:1]-[#9:2]", 99.0, 99.0],
        ["[#16:1]-[#17:2]", 99.0, 99.0],
        ["[#16:1]-[#35:2]", 99.0, 99.0],
        ["[#16:1]-[#53:2]", 99.0, 99.0],
        ["[#16X2,#16X1-1,#16X3+1:1]-[#6X4:2]", 99.0, 99.0],
        ["[#16X2,#16X1-1,#16X3+1:1]-[#6X3:2]", 99.0, 99.0],
        ["[#16X2:1]-[#7:2]", 99.0, 99.0],
        ["[#16X2:1]-[#8X2:2]", 99.0, 99.0],
        ["[#16X2:1]=[#8X1,#7X2:2]", 99.0, 99.0],
        ["[#16X4,#16X3!+1:1]-[#6:2]", 99.0, 99.0],
        ["[#16X4,#16X3:1]~[#7:2]", 99.0, 99.0],
        ["[#16X4,#16X3:1]-[#8X2:2]", 99.0, 99.0],
        ["[#16X4,#16X3:1]~[#8X1:2]", 99.0, 99.0],
        ["[#15:1]-[#1:2]", 99.0, 99.0],
        ["[#15:1]~[#6:2]", 99.0, 99.0],
        ["[#15:1]-[#7:2]", 99.0, 99.0],
        ["[#15:1]=[#7:2]", 99.0, 99.0],
        ["[#15:1]~[#8X2:2]", 99.0, 99.0],
        ["[#15:1]~[#8X1:2]", 99.0, 99.0],
        ["[#16:1]-[#15:2]", 99.0, 99.0],
        ["[#15:1]=[#16X1:2]", 99.0, 99.0],
        ["[#6:1]-[#9:2]", 99.0, 99.0],
        ["[#6X4:1]-[#9:2]", 0.6, 0.7],
        ["[#6:1]-[#17:2]", 99.0, 99.0],
        ["[#6X4:1]-[#17:2]", 99.0, 99.0],
        ["[#6:1]-[#35:2]", 99.0, 99.0],
        ["[#6X4:1]-[#35:2]", 99.0, 99.0],
        ["[#6:1]-[#53:2]", 99.0, 99.0],
        ["[#6X4:1]-[#53:2]", 99.0, 99.0],
        ["[#7:1]-[#9:2]", 99.0, 99.0],
        ["[#7:1]-[#17:2]", 99.0, 99.0],
        ["[#7:1]-[#35:2]", 99.0, 99.0],
        ["[#7:1]-[#53:2]", 99.0, 99.0],
        ["[#15:1]-[#9:2]", 99.0, 99.0],
        ["[#15:1]-[#17:2]", 99.0, 99.0],
        ["[#15:1]-[#35:2]", 99.0, 99.0],
        ["[#15:1]-[#53:2]", 99.0, 99.0],
        ["[#6X4:1]-[#1:2]", 99.0, 99.0],
        ["[#6X3:1]-[#1:2]", 99.0, 99.0],
        ["[#6X2:1]-[#1:2]", 99.0, 99.0],
        ["[#7:1]-[#1:2]", 99.0, 99.0],
        ["[#8:1]-[#1:2]", 99.0, 99.1],
    ]

    smirks = [x[0] for x in patterns]
    params = np.array([[x[1], x[2]] for x in patterns])
    hbh = bonded.HarmonicBondHandler(smirks, params, None)

    obj = hbh.serialize()
    all_handlers = deserialize_handlers(bin_to_str(obj))

    assert len(all_handlers) == 1

    new_hbh = all_handlers[0]
    np.testing.assert_equal(new_hbh.smirks, hbh.smirks)
    np.testing.assert_equal(new_hbh.params, hbh.params)

    assert new_hbh.props == hbh.props