Ejemplo n.º 1
0
def do_deletion(
    x0,
    v0,
    combined_bps,
    combined_masses,
    box,
    guest_name,
    leg_type,
    u_impls,
    deletion_steps,
):
    seed = 2021
    intg = LangevinIntegrator(300.0, 1.5e-3, 1.0, combined_masses, seed).impl()

    ctxt = custom_ops.Context(x0, v0, box, intg, u_impls)

    # du_dl_obs = custom_ops.FullPartialUPartialLambda(u_impls, subsample_freq)
    # ctxt.add_observable(du_dl_obs)

    deletion_lambda_schedule = np.linspace(MIN_LAMBDA, DELETION_MAX_LAMBDA,
                                           deletion_steps)

    subsample_freq = 1
    full_du_dls, _, _ = ctxt.multiple_steps(deletion_lambda_schedule,
                                            subsample_freq)

    step = len(deletion_lambda_schedule) - 1
    lamb = deletion_lambda_schedule[-1]
    ctxt.step(lamb)
    report.report_step(
        ctxt,
        step,
        lamb,
        box,
        combined_bps,
        u_impls,
        guest_name,
        deletion_steps,
        f"{leg_type.upper()}_DELETION",
    )

    if report.too_much_force(ctxt, lamb, box, combined_bps, u_impls):
        print("Not calculating work (too much force)")
        return None

    # Note: this condition only applies for ABFE, not RBFE
    if abs(full_du_dls[0]) > 0.001 or abs(full_du_dls[-1]) > 0.001:
        print("Not calculating work (du_dl endpoints are not ~0)")
        return None

    work = np.trapz(full_du_dls, deletion_lambda_schedule[::subsample_freq])
    print(f"guest_name: {guest_name}\t{leg_type}_work: {work:.2f}")
    return work
Ejemplo n.º 2
0
def do_switch(
    x0,
    v0,
    combined_bps,
    combined_masses,
    box,
    guest_name,
    leg_type,
    u_impls,
    transition_steps,
):
    seed = 2021
    intg = LangevinIntegrator(300.0, 1.5e-3, 1.0, combined_masses, seed).impl()

    ctxt = custom_ops.Context(x0, v0, box, intg, u_impls)

    switching_lambda_schedule = np.linspace(MIN_LAMBDA, MAX_LAMBDA,
                                            transition_steps)

    subsample_interval = 1
    full_du_dls, _, _ = ctxt.multiple_steps(switching_lambda_schedule,
                                            subsample_interval)

    step = len(switching_lambda_schedule) - 1
    lamb = switching_lambda_schedule[-1]
    ctxt.step(lamb)
    report.report_step(
        ctxt,
        step,
        lamb,
        box,
        combined_bps,
        u_impls,
        guest_name,
        transition_steps,
        f"{leg_type.upper()}_SWITCH",
    )

    if report.too_much_force(ctxt, lamb, box, combined_bps, u_impls):
        return

    work = np.trapz(full_du_dls,
                    switching_lambda_schedule[::subsample_interval])
    print(f"guest_name: {guest_name}\t{leg_type}_work: {work:.2f}")
    return work
Ejemplo n.º 3
0
def run_leg(
    combined_coords,
    combined_bps,
    combined_masses,
    host_box,
    guest_name,
    leg_type,
    num_switches,
    transition_steps,
):
    x0 = combined_coords
    v0 = np.zeros_like(x0)
    print(
        f"{leg_type.upper()}_SYSTEM",
        f"guest_name: {guest_name}",
        f"num_atoms: {len(x0)}",
    )

    seed = 2021
    intg = LangevinIntegrator(300.0, 1.5e-3, 1.0, combined_masses, seed).impl()

    u_impls = []
    for bp in combined_bps:
        bp_impl = bp.bound_impl(precision=np.float32)
        u_impls.append(bp_impl)

    ctxt = custom_ops.Context(x0, v0, host_box, intg, u_impls)

    # TODO: pre-equilibrate?

    # equilibrate & shoot off switching jobs
    steps_per_batch = 1001

    works = []
    for b in range(num_switches):
        equil2_lambda_schedule = np.ones(steps_per_batch) * MIN_LAMBDA
        ctxt.multiple_steps(equil2_lambda_schedule, 0)
        lamb = equil2_lambda_schedule[-1]
        step = len(equil2_lambda_schedule) - 1
        report.report_step(
            ctxt,
            (b + 1) * step,
            lamb,
            host_box,
            combined_bps,
            u_impls,
            guest_name,
            num_switches * steps_per_batch,
            f"{leg_type.upper()}_EQUILIBRATION_2",
        )

        if report.too_much_force(ctxt, MIN_LAMBDA, host_box, combined_bps,
                                 u_impls):
            return

        work = do_switch(
            ctxt.get_x_t(),
            ctxt.get_v_t(),
            combined_bps,
            combined_masses,
            host_box,
            guest_name,
            leg_type,
            u_impls,
            transition_steps,
        )
        works.append(work)

    return works
Ejemplo n.º 4
0
def dock_and_equilibrate(
    host_pdbfile,
    guests_sdfile,
    max_lambda,
    insertion_steps,
    eq_steps,
    outdir,
    fewer_outfiles=False,
    constant_atoms=[],
):
    """Solvates a host, inserts guest(s) into solvated host, equilibrates

    Parameters
    ----------

    host_pdbfile: path to host pdb file to dock into
    guests_sdfile: path to input sdf with guests to pose/dock
    max_lambda: lambda value the guest should insert from or delete to
        (recommended: 1.0 for work calulation, 0.25 to stay close to original pose)
        (must be =1 for work calculation to be applicable)
    insertion_steps: how many steps to insert the guest over (recommended: 501)
    eq_steps: how many steps of equilibration to do after insertion (recommended: 15001)
    outdir: where to write output (will be created if it does not already exist)
    fewer_outfiles: if True, will only write frames for the equilibration, not insertion
    constant_atoms: atom numbers from the host_pdbfile to hold mostly fixed across the simulation
        (1-indexed, like PDB files)

    Output
    ------

    A pdb & sdf file for the last step of insertion
       (outdir/<guest_name>/<guest_name>_ins_<step>_[host.pdb/guest.sdf])
    A pdb & sdf file every 1000 steps of equilibration
       (outdir/<guest_name>/<guest_name>_eq_<step>_[host.pdb/guest.sdf])
    stdout corresponding to the files written noting the lambda value and energy
    stdout for each guest noting the work of transition, if applicable
    stdout for each guest noting how long it took to run

    Note
    ----
    The work will not be calculated if the du_dl endpoints are not close to 0 or if any norm of
    force per atom exceeds 20000 kJ/(mol*nm) [MAX_NORM_FORCE defined in docking/report.py]
    """

    if not os.path.exists(outdir):
        os.makedirs(outdir)

    print(f"""
    HOST_PDBFILE = {host_pdbfile}
    GUESTS_SDFILE = {guests_sdfile}
    OUTDIR = {outdir}
    MAX_LAMBDA = {max_lambda}
    INSERTION_STEPS = {insertion_steps}
    EQ_STEPS = {eq_steps}
    """)

    # Prepare host
    # TODO: handle extra (non-transitioning) guests?
    print("Solvating host...")
    (
        solvated_host_system,
        solvated_host_coords,
        _,
        _,
        host_box,
        solvated_topology,
    ) = builders.build_protein_system(host_pdbfile)

    _, solvated_host_pdb = tempfile.mkstemp(suffix=".pdb", text=True)
    writer = pdb_writer.PDBWriter([solvated_topology], solvated_host_pdb)
    writer.write_frame(solvated_host_coords)
    writer.close()
    solvated_host_mol = Chem.MolFromPDBFile(solvated_host_pdb, removeHs=False)
    os.remove(solvated_host_pdb)

    ff = Forcefield.load_from_file("smirnoff_1_1_0_ccc.py")

    # Run the procedure
    print("Getting guests...")
    suppl = Chem.SDMolSupplier(guests_sdfile, removeHs=False)
    for guest_mol in suppl:
        start_time = time.time()
        guest_name = guest_mol.GetProp("_Name")
        guest_conformer = guest_mol.GetConformer(0)
        orig_guest_coords = np.array(guest_conformer.GetPositions(),
                                     dtype=np.float64)
        orig_guest_coords = orig_guest_coords / 10  # convert to md_units

        minimized_coords = minimizer.minimize_host_4d([guest_mol],
                                                      solvated_host_system,
                                                      solvated_host_coords, ff,
                                                      host_box)

        afe = free_energy.AbsoluteFreeEnergy(guest_mol, ff)

        ups, sys_params, combined_masses, _ = afe.prepare_host_edge(
            ff.get_ordered_params(), solvated_host_system, minimized_coords)

        combined_bps = []
        for up, sp in zip(ups, sys_params):
            combined_bps.append(up.bind(sp))

        x0 = np.concatenate([minimized_coords, orig_guest_coords])
        v0 = np.zeros_like(x0)
        print("SYSTEM", f"guest_name: {guest_name}", f"num_atoms: {len(x0)}")

        for atom_num in constant_atoms:
            combined_masses[atom_num - 1] += 50000

        seed = 2021
        intg = LangevinIntegrator(300.0, 1.5e-3, 1.0, combined_masses,
                                  seed).impl()

        u_impls = []
        for bp in combined_bps:
            bp_impl = bp.bound_impl(precision=np.float32)
            u_impls.append(bp_impl)

        ctxt = custom_ops.Context(x0, v0, host_box, intg, u_impls)

        # insert guest
        insertion_lambda_schedule = np.linspace(max_lambda, 0.0,
                                                insertion_steps)
        calc_work = True

        # collect a du_dl calculation once every other step
        subsample_interval = 1

        full_du_dls, _, _ = ctxt.multiple_steps(insertion_lambda_schedule,
                                                subsample_interval)
        step = len(insertion_lambda_schedule) - 1
        lamb = insertion_lambda_schedule[-1]
        ctxt.step(lamb)

        report.report_step(
            ctxt,
            step,
            lamb,
            host_box,
            combined_bps,
            u_impls,
            guest_name,
            insertion_steps,
            "INSERTION",
        )
        if not fewer_outfiles:
            host_coords = ctxt.get_x_t()[:len(solvated_host_coords)] * 10
            guest_coords = ctxt.get_x_t()[len(solvated_host_coords):] * 10
            report.write_frame(
                host_coords,
                solvated_host_mol,
                guest_coords,
                guest_mol,
                guest_name,
                outdir,
                str(step).zfill(len(str(insertion_steps))),
                "ins",
            )

        if report.too_much_force(ctxt, lamb, host_box, combined_bps, u_impls):
            print("Not calculating work (too much force)")
            calc_work = False
            continue

        # Note: this condition only applies for ABFE, not RBFE
        if abs(full_du_dls[0]) > 0.001 or abs(full_du_dls[-1]) > 0.001:
            print("Not calculating work (du_dl endpoints are not ~0)")
            calc_work = False

        if calc_work:
            work = np.trapz(full_du_dls,
                            insertion_lambda_schedule[::subsample_interval])
            print(f"guest_name: {guest_name}\tinsertion_work: {work:.2f}")

        # equilibrate
        for step in range(eq_steps):
            ctxt.step(0.00)
            if step % 1000 == 0:
                report.report_step(
                    ctxt,
                    step,
                    0.00,
                    host_box,
                    combined_bps,
                    u_impls,
                    guest_name,
                    eq_steps,
                    "EQUILIBRATION",
                )
                if (not fewer_outfiles) or (step == eq_steps - 1):
                    host_coords = ctxt.get_x_t()[:len(solvated_host_coords
                                                      )] * 10
                    guest_coords = ctxt.get_x_t()[len(solvated_host_coords
                                                      ):] * 10
                    report.write_frame(
                        host_coords,
                        solvated_host_mol,
                        guest_coords,
                        guest_mol,
                        guest_name,
                        outdir,
                        str(step).zfill(len(str(eq_steps))),
                        "eq",
                    )
            if step in (0, int(eq_steps / 2), eq_steps - 1):
                if report.too_much_force(ctxt, 0.00, host_box, combined_bps,
                                         u_impls):
                    break

        end_time = time.time()
        print(f"{guest_name} took {(end_time - start_time):.2f} seconds")
Ejemplo n.º 5
0
def run_leg(
    orig_host_coords,
    orig_guest_coords,
    combined_bps,
    combined_masses,
    host_box,
    guest_name,
    leg_type,
    host_mol,
    guest_mol,
    outdir,
    num_deletions,
    deletion_steps,
    insertion_max_lambda,
    insertion_steps,
    eq1_steps,
    fewer_outfiles=False,
    no_outfiles=False,
):
    x0 = np.concatenate([orig_host_coords, orig_guest_coords])
    v0 = np.zeros_like(x0)
    print(
        f"{leg_type.upper()}_SYSTEM",
        f"guest_name: {guest_name}",
        f"num_atoms: {len(x0)}",
    )

    seed = 2021
    intg = LangevinIntegrator(300.0, 1.5e-3, 1.0, combined_masses, seed).impl()

    u_impls = []
    for bp in combined_bps:
        bp_impl = bp.bound_impl(precision=np.float32)
        u_impls.append(bp_impl)

    ctxt = custom_ops.Context(x0, v0, host_box, intg, u_impls)

    # insert guest
    insertion_lambda_schedule = np.linspace(insertion_max_lambda, MIN_LAMBDA,
                                            insertion_steps)

    ctxt.multiple_steps(insertion_lambda_schedule, 0)  # do not collect du_dls

    lamb = insertion_lambda_schedule[-1]
    step = len(insertion_lambda_schedule) - 1

    report.report_step(
        ctxt,
        step,
        lamb,
        host_box,
        combined_bps,
        u_impls,
        guest_name,
        insertion_steps,
        f"{leg_type.upper()}_INSERTION",
    )
    if not fewer_outfiles and not no_outfiles:
        host_coords = ctxt.get_x_t()[:len(orig_host_coords)] * 10
        guest_coords = ctxt.get_x_t()[len(orig_host_coords):] * 10
        report.write_frame(
            host_coords,
            host_mol,
            guest_coords,
            guest_mol,
            guest_name,
            outdir,
            str(step).zfill(len(str(insertion_steps))),
            f"{leg_type}-ins",
        )
    if report.too_much_force(ctxt, lamb, host_box, combined_bps, u_impls):
        return []

    # equilibrate
    equil_lambda_schedule = np.ones(eq1_steps) * MIN_LAMBDA
    lamb = equil_lambda_schedule[-1]
    step = len(equil_lambda_schedule) - 1
    ctxt.multiple_steps(equil_lambda_schedule, 0)
    report.report_step(
        ctxt,
        step,
        MIN_LAMBDA,
        host_box,
        combined_bps,
        u_impls,
        guest_name,
        eq1_steps,
        f"{leg_type.upper()}_EQUILIBRATION_1",
    )
    if not fewer_outfiles and not no_outfiles:
        host_coords = ctxt.get_x_t()[:len(orig_host_coords)] * 10
        guest_coords = ctxt.get_x_t()[len(orig_host_coords):] * 10
        report.write_frame(
            host_coords,
            host_mol,
            guest_coords,
            guest_mol,
            guest_name,
            outdir,
            str(step).zfill(len(str(eq1_steps))),
            f"{leg_type}-eq1",
        )
    if report.too_much_force(ctxt, MIN_LAMBDA, host_box, combined_bps,
                             u_impls):
        print("Too much force")
        return []

    # equilibrate more & shoot off deletion jobs
    steps_per_batch = 1001
    works = []
    for b in range(num_deletions):
        deletion_lambda_schedule = np.ones(steps_per_batch) * MIN_LAMBDA

        ctxt.multiple_steps(deletion_lambda_schedule, 0)
        lamb = deletion_lambda_schedule[-1]
        step = len(deletion_lambda_schedule) - 1
        report.report_step(
            ctxt,
            (b + 1) * step,
            MIN_LAMBDA,
            host_box,
            combined_bps,
            u_impls,
            guest_name,
            num_deletions * steps_per_batch,
            f"{leg_type.upper()}_EQUILIBRATION_2",
        )

        # TODO: if guest has undocked, stop simulation
        if not no_outfiles:
            host_coords = ctxt.get_x_t()[:len(orig_host_coords)] * 10
            guest_coords = ctxt.get_x_t()[len(orig_host_coords):] * 10
            report.write_frame(
                host_coords,
                host_mol,
                guest_coords,
                guest_mol,
                guest_name,
                outdir,
                str((b + 1) * step).zfill(
                    len(str(num_deletions * steps_per_batch))),
                f"{leg_type}-eq2",
            )
        if report.too_much_force(ctxt, MIN_LAMBDA, host_box, combined_bps,
                                 u_impls):
            print("Too much force")
            return works

        work = do_deletion(
            ctxt.get_x_t(),
            ctxt.get_v_t(),
            combined_bps,
            combined_masses,
            host_box,
            guest_name,
            leg_type,
            u_impls,
            deletion_steps,
        )
        works.append(work)

    return works