Exemplo n.º 1
0
def test_run_bace_spectator():
    """
    Ensure that we can instantiate and run a repex relative free energy calculation the cdk2 ligands in vacuum
    """
    # Enter a temporary directory
    from perses.tests.utils import enter_temp_directory
    with enter_temp_directory() as tmpdirname:
        # Move to temporary directory
        print(f'Running example in temporary directory: {tmpdirname}')

        # Setup directory
        setup_directory = resource_filename("perses", "data/bace-example")
        print(f'Setup directory : {setup_directory}')

        # Get options
        from perses.app.setup_relative_calculation import getSetupOptions
        yaml_filename = os.path.join(setup_directory, "bace_setup.yaml")
        setup_options = getSetupOptions(yaml_filename)

        # DEBUG: Print traceback for any UserWarnings
        show_warning_stacktraces = False
        if show_warning_stacktraces:
            import traceback
            import warnings
            _old_warn = warnings.warn

            def warn(*args, **kwargs):
                tb = traceback.extract_stack()
                _old_warn(*args, **kwargs)
                print("".join(traceback.format_list(tb)[:-1]))

            warnings.warn = warn

        setup_options['scheduler_address'] = None
        for parameter in ['protein_pdb', 'ligand_file']:
            setup_options[parameter] = os.path.join(setup_directory,
                                                    setup_options[parameter])
            # only one spectator
            setup_options['spectators'] = [
                os.path.join(setup_directory, setup_options['spectators'][0])
            ]
        for parameter in [
                'trajectory_directory', 'trajectory_prefix',
                'save_setup_pickle_as'
        ]:
            setup_options[parameter] = os.path.join(tmpdirname,
                                                    setup_options[parameter])

        # Run setup
        n_iterations = 2
        setup_dict = setup_relative_calculation.run_setup(setup_options)
        setup_dict['hybrid_samplers']['complex'].run(n_iterations=n_iterations)

        # test that there is TLA in the complex system
        found_tla = False
        for res in setup_dict['hybrid_topology_factories'][
                'complex'].hybrid_topology.residues:
            if res.name == 'TLA':
                found_tla = True
        assert found_tla == True, 'Spectator TLA not in old topology'
Exemplo n.º 2
0
def test_run_cdk2_iterations_repex():
    """
    Ensure that we can instantiate and run a repex relative free energy calculation the cdk2 ligands in vacuum
    """
    # Enter a temporary directory
    from perses.tests.utils import enter_temp_directory
    with enter_temp_directory() as tmpdirname:
        # Move to temporary directory
        print(f'Running example in temporary directory: {tmpdirname}')

        # Setup directory
        setup_directory = resource_filename("perses", "data/cdk2-example")

        # Get options
        from perses.app.setup_relative_calculation import getSetupOptions
        yaml_filename = os.path.join(setup_directory, "cdk2_setup_repex.yaml")
        setup_options = getSetupOptions(yaml_filename)

        # DEBUG: Print traceback for any UserWarnings
        show_warning_stacktraces = False
        if show_warning_stacktraces:
            import traceback
            import warnings
            _old_warn = warnings.warn

            def warn(*args, **kwargs):
                tb = traceback.extract_stack()
                _old_warn(*args, **kwargs)
                print("".join(traceback.format_list(tb)[:-1]))

            warnings.warn = warn

        # Update options
        #setup_options['solvate'] = False
        #setup_options['n_cycles'] = 2
        setup_options['scheduler_address'] = None
        for parameter in ['protein_pdb', 'ligand_file']:
            setup_options[parameter] = os.path.join(setup_directory,
                                                    setup_options[parameter])
        for parameter in ['trajectory_directory', 'save_setup_pickle_as']:
            setup_options[parameter] = os.path.join(tmpdirname,
                                                    setup_options[parameter])

        #length_of_protocol = setup_options['n_steps_ncmc_protocol']
        #write_interval = setup_options['n_steps_per_move_application']
        #n_work_values_per_iteration = length_of_protocol // write_interval

        # Run setup
        setup_dict = setup_relative_calculation.run_setup(setup_options)
        setup_dict['hybrid_samplers']['solvent'].run(n_iterations=n_iterations)
def test_run_cdk2_iterations():
    """
    Ensure that we can instantiate and run the cdk2 ligands in vacuum
    """
    setup_directory = resource_filename("perses", "data/cdk2-example")
    os.chdir(setup_directory)
    n_iterations = 2

    yaml_filename = "cdk2_setup.yaml"
    yaml_file = open(yaml_filename, "r")
    setup_options = yaml.safe_load(yaml_file)
    yaml_file.close()

    if not os.path.exists(setup_options['trajectory_directory']):
        os.makedirs(setup_options['trajectory_directory'])

    setup_options['solvate'] = False
    setup_options['scheduler_address'] = None

    length_of_protocol = setup_options['n_steps_ncmc_protocol']
    write_interval = setup_options['n_steps_per_move_application']

    n_work_values_per_iteration = length_of_protocol // write_interval

    setup_dict = setup_relative_calculation.run_setup(setup_options)

    setup_dict['ne_fep']['solvent'].run(n_iterations=n_iterations)

    #now check that the correct number of iterations was written out:
    os.chdir(setup_options['trajectory_directory'])
    import glob

    #for the verification of work writing, we add one to the work dimension, since the first work value is always zero

    #check for lambda zero
    lambda_zero_filenames = glob.glob("*0.cw.npy")
    lambda_zero_npy = np.stack(
        [np.load(filename) for filename in lambda_zero_filenames])
    assert np.shape(lambda_zero_npy) == (n_iterations,
                                         n_work_values_per_iteration + 1)

    #check for lambda one
    lambda_one_filenames = glob.glob("*1.cw.npy")
    lambda_one_npy = np.stack(
        [np.load(filename) for filename in lambda_one_filenames])
    assert np.shape(lambda_one_npy) == (n_iterations,
                                        n_work_values_per_iteration + 1)
Exemplo n.º 4
0
def run_neq_fah_setup(ligand_file,
                      old_ligand_index,
                      new_ligand_index,
                      forcefield_files,
                      trajectory_directory,
                      complex_box_dimensions=(9.8, 9.8, 9.8),
                      solvent_box_dimensions=(3.5, 3.5, 3.5),
                      timestep=4.0,
                      eq_splitting='V R O R V',
                      neq_splitting='V R H O R V',
                      measure_shadow_work=False,
                      pressure=1.0,
                      temperature=300. * unit.kelvin,
                      solvent_padding=9 * unit.angstroms,
                      phases=['complex', 'solvent', 'vacuum'],
                      phase_project_ids=None,
                      protein_pdb=None,
                      receptor_mol2=None,
                      small_molecule_forcefield='openff-1.2.0',
                      small_molecule_parameters_cache=None,
                      atom_expression=['IntType'],
                      bond_expression=['DefaultBonds'],
                      spectators=None,
                      neglect_angles=False,
                      anneal_14s=False,
                      nonbonded_method='PME',
                      map_strength=None,
                      softcore_v2=False,
                      save_setup_pickle_as=None,
                      render_atom_map=False,
                      alchemical_functions=DEFAULT_ALCHEMICAL_FUNCTIONS,
                      num_equilibration_iterations=1000,
                      num_equilibration_steps_per_iteration=250,
                      nsteps_eq=250000,
                      nsteps_neq=250000,
                      fe_type='fah',
                      collision_rate=1. / unit.picoseconds,
                      collision_rate_setup=90. / unit.picoseconds,
                      constraint_tolerance=1e-6,
                      n_steps_per_move_application=250,
                      globalVarFreq=250,
                      setup='small_molecule',
                      protein_kwargs=None,
                      ionic_strength=0.15 * unit.molar,
                      remove_constraints='not water',
                      **kwargs):
    """
    main execution function that will:
        - create a directory for each phase according to the `trajectory_directory` argument
        - make a subdirectory named f"RUN_{old_ligand_index}_{new_ligand_index}" given the specified ligand indices of the `ligand_file`
        - create topology proposals for all phases
        - create/serialize hybrid factories or all phases (and validate endstates)
        - create/serialize an openmmtools.integrators.PeriodicNonequilibriumIntegrator for all phases
        - relax generated structures with a minimizer and LangevinIntegrator for all phases
        - create/serialize a state associated with the relaxed structures
        - create/serialize a `core.xml` object for all phases


    >>> run_neq_fah_setup('ligand.sdf', 0, 1,['amber/ff14SB.xml','amber/tip3p_standard.xml','amber/tip3p_HFE_multivalent.xml'],'RUN0',protein_pdb='protein.pdb', phases=['complex','solvent','vacuum'],phase_project_ids={'complex':14320,'solvent':14321,'vacuum':'vacuum'})

    arguments
        ligand_file : str
            .sdf (or any openeye-readable) file containing ligand labeled indices and structures
        old_ligand_index : int
            index of the old ligand
        new_ligand_index : int
            inded of the new ligand
        forcefield_files : list of str
            list of forcefields to use for complex/solvent parameterization
        trajectory_directory : str
            RUNXXX for FAH deployment
        complex_box_dimensions : Vec3, default=(9.8, 9.8, 9.8)
            define box dimensions of complex phase (in nm)
        solvent_box_dimensions : Vec3, default=(3.5, 3.5, 3.5)
            define box dimensions of solvent phase (in nm)
        timestep : float, default=4.
            step size of nonequilibrium integration
        eq_splitting : str, default = 'V R O R V'
            splitting string of relaxation dynamics
        neq_splitting : str, default = 'V R H O R V'
            splitting string of nonequilibrium dynamics
        measure_shadow_work : bool, default=False
            True/False to measure shadow work
        pressure: float, default=1.
            pressure in atms for simulation
        temperature: simtk.unit.Quantity, default=300.*unit.kelvin,
            temperature in K for simulation
        phases: list, default = ['complex','solvent','vacuum','apo']
            phases to run, where allowed phases are:
            'complex','solvent','vacuum','apo'
        protein_pdb : str, default=None
            name of protein file
        receptor_mol2 : str, default=None
            name of receptor file if protein_pdb not provided
        small_molecule_forcefield : str, default='openff-1.0.0'
            small molecule forcefield filename
        small_molecule_parameters_cache : str, default=None
            cache file containing small molecule forcefield files
        atom_expression : list default=['IntType']
            list of string for atom mapping criteria. see oechem.OEExprOpts for options
        bond_expression : list default=['DefaultBonds']
            list of string for bond mapping criteria. see oechem.OEExprOpts for options
        map_strength : 'str', default=None
            atom and bond expressions will be ignored, and either a 'weak', 'default' or 'strong' map_strength will be used.
        spectators : str, default=None
            path to any non-alchemical atoms in simulation
        neglect_angles : bool, default=False
            wether to use angle terms in building of unique-new groups. False is strongly recommended
        anneal_14s : bool, default False
            Whether to anneal 1,4 interactions over the protocol;
        nonbonded_method : str, default='PME'
            nonbonded method to use
        softcore_v2=bool, default=False
            wether to use v2 softcore
        alchemical_functions : dict, default=DEFAULT_ALCHEMICAL_FUNCTIONS
            alchemical functions for transformation
        num_equilibration_iterations: int, default=1000
            number of equilibration steps to do during set up
        num_equilibration_steps_per_iteration: int, default=250,
            number of steps per iteration. default is 250 steps of 2fs, 1000 times which is 500ps of equilibration for SETUP
        nsteps_eq : int, default=250000
            number of normal MD steps to take for FAH integrator for PRODUCTION
        nsteps_neq : int, default=250000
            number of nonequilibrium steps to take for FAH integrator for PRODUCTION
        fe_type : str, default='fah'
            tells setup_relative_calculation() to use the fah pipeline
        collision_rate : simtk.unit.Quantity, default=1./unit.picosecond
            collision_rate for PRODUCTION
        collision_rate_setup : simtk.unit.Quantity, default=90./unit.picosecond
        constraint_tolerance : float, default=1e-6
            tolerance to use for constraints
        n_steps_per_move_application : int default=250
            number of equilibrium steps to take per move
    """
    from perses.utils import data
    if isinstance(temperature, float) or isinstance(temperature, int):
        temperature = temperature * unit.kelvin

    if isinstance(timestep, float) or isinstance(timestep, int):
        timestep = timestep * unit.femtosecond

    if isinstance(pressure, float) or isinstance(pressure, int):
        pressure = pressure * unit.atmosphere

    #turn all of the args into a dict for passing to run_setup
    # HBM - this doesn't feel particularly safe
    # Also, this means that the function can't run without being called by run(), as we are requiring things that aren't arguments to this function, like 'solvent_projid'...etc
    setup_options = locals()
    if 'kwargs' in setup_options.keys(
    ):  #update the setup options w.r.t. kwargs
        setup_options.update(setup_options['kwargs'])
    if protein_kwargs is not None:  #update the setup options w.r.t. the protein kwargs
        setup_options.update(setup_options['protein_kwargs'])
        if 'apo_box_dimensions' not in list(setup_options.keys()):
            setup_options['apo_box_dimensions'] = setup_options[
                'complex_box_dimensions']

    #setups_allowed
    setups_allowed = ['small_molecule', 'protein']
    assert setup in setups_allowed, f"setup {setup} not in setups_allowed: {setups_allowed}"

    # check there is a project_id for each phase
    for phase in phases:
        assert (
            phase in phase_project_ids
        ), f"Phase {phase} requested, but not in phase_project_ids {phase_project_ids.keys()}"

    #some modification for fah-specific functionality:
    setup_options['trajectory_prefix'] = None
    setup_options['anneal_1,4s'] = False
    from perses.utils.openeye import generate_expression
    setup_options['atom_expr'] = generate_expression(
        setup_options['atom_expression'])
    setup_options['bond_expr'] = generate_expression(
        setup_options['bond_expression'])

    #run the run_setup to generate topology proposals and htfs
    _logger.info(f"spectators: {setup_options['spectators']}")
    if setup == 'small_molecule':
        from perses.app.setup_relative_calculation import run_setup
        setup_dict = run_setup(setup_options,
                               serialize_systems=False,
                               build_samplers=False)
        topology_proposals = setup_dict['topology_proposals']
        htfs = setup_dict['hybrid_topology_factories']
    elif setup == 'protein':
        from perses.app.relative_point_mutation_setup import PointMutationExecutor
        setup_engine = PointMutationExecutor(**setup_options)
        topology_proposals = {
            'complex': setup_engine.get_complex_htf()._topology_proposal,
            'apo': setup_engine.get_apo_htf()._topology_proposal
        }
        htfs = {
            'complex': setup_engine.get_complex_htf(),
            'apo': setup_engine.get_apo_htf()
        }

    #create solvent and complex directories
    for phase in htfs.keys():
        _logger.info(f'Setting up phase {phase}')
        phase_dir = f"{phase_project_ids[phase]}/RUNS"
        dir = os.path.join(os.getcwd(), phase_dir, trajectory_directory)
        if not os.path.exists(dir):
            os.makedirs(dir)

        # TODO - replace this with actually saving the importand part of the HTF
        np.savez_compressed(f'{dir}/htf', htfs[phase])

        #serialize the hybrid_system
        data.serialize(htfs[phase].hybrid_system, f"{dir}/system.xml.bz2")

        #make and serialize an integrator
        integrator = make_neq_integrator(**setup_options)
        data.serialize(integrator, f"{dir}/integrator.xml")

        #create and serialize a state
        try:
            state = relax_structure(
                temperature=temperature,
                system=htfs[phase].hybrid_system,
                positions=htfs[phase].hybrid_positions,
                nequil=num_equilibration_iterations,
                n_steps_per_iteration=num_equilibration_steps_per_iteration,
                collision_rate=collision_rate_setup,
                **kwargs)

            data.serialize(state, f"{dir}/state.xml.bz2")
        except Exception as e:
            _logger.warning(e)
            passed = False
        else:
            passed = True

        pos = state.getPositions(asNumpy=True)
        pos = np.asarray(pos)

        import mdtraj as md
        top = htfs[phase].hybrid_topology
        np.save(f'{dir}/hybrid_topology', top)
        traj = md.Trajectory(pos, top)
        traj.remove_solvent(exclude=['CL', 'NA'], inplace=True)
        traj.save(f'{dir}/hybrid_{phase}.pdb')

        #lastly, make a core.xml
        ###
        nsteps_per_cycle = 2 * nsteps_eq + 2 * nsteps_neq
        ncycles = 1
        nsteps_per_ps = 250
        nsteps = ncycles * nsteps_per_cycle
        make_core_file(numSteps=nsteps,
                       xtcFreq=1000 * nsteps_per_ps,
                       globalVarFreq=10 * nsteps_per_ps,
                       directory=dir)

        #create a logger for reference
        # TODO - add more details to this
        references = {
            'start_ligand': old_ligand_index,
            'end_ligand': new_ligand_index,
            'protein_pdb': protein_pdb,
            'passed_strucutre_relax': passed
        }

        np.save(f'{dir}/references', references)

        tp = topology_proposals
        from perses.utils.smallmolecules import render_atom_mapping
        atom_map_filename = f'{dir}/atom_map.png'
        if setup == 'protein':
            from perses.utils.smallmolecules import render_protein_residue_atom_mapping
            render_protein_residue_atom_mapping(tp['apo'], atom_map_filename)
        else:
            old_ligand_oemol, new_ligand_oemol = tp['ligand_oemol_old'], tp[
                'ligand_oemol_new']
            _map = tp['non_offset_new_to_old_atom_map']
            render_atom_mapping(atom_map_filename, old_ligand_oemol,
                                new_ligand_oemol, _map)
Exemplo n.º 5
0
def run_neq_fah_setup(ligand_file,
                      old_ligand_index,
                      new_ligand_index,
                      forcefield_files,
                      trajectory_directory,
                      complex_box_dimensions=(9.8, 9.8, 9.8),
                      solvent_box_dimensions=(3.5, 3.5, 3.5),
                      timestep=4.0 * unit.femtosecond,
                      eq_splitting='V R O R V',
                      neq_splitting='V R H O R V',
                      measure_shadow_work=False,
                      pressure=1.0,
                      temperature=300,
                      solvent_padding=9 * unit.angstroms,
                      phases=['complex', 'solvent', 'vacuum'],
                      protein_pdb=None,
                      receptor_mol2=None,
                      small_molecule_forcefield='openff-1.0.0',
                      small_molecule_parameters_cache=None,
                      atom_expression=['IntType'],
                      bond_expression=['DefaultBonds'],
                      spectators=None,
                      neglect_angles=False,
                      anneal_14s=False,
                      nonbonded_method='PME',
                      map_strength=None,
                      softcore_v2=False,
                      save_setup_pickle_as=None,
                      render_atom_map=False,
                      alchemical_functions=DEFAULT_ALCHEMICAL_FUNCTIONS,
                      num_equilibration_iterations=1000,
                      num_equilibration_steps_per_iteration=250,
                      nsteps_eq=250000,
                      nsteps_neq=250000,
                      fe_type='fah',
                      collision_rate=1. / unit.picoseconds,
                      collision_rate_setup=90. / unit.picoseconds,
                      constraint_tolerance=1e-6,
                      n_steps_per_move_application=250,
                      globalVarFreq=250,
                      **kwargs):
    """
    main execution function that will:
        - create a directory for each phase according to the `trajectory_directory` argument
        - make a subdirectory named f"RUN_{old_ligand_index}_{new_ligand_index}" given the specified ligand indices of the `ligand_file`
        - create topology proposals for all phases
        - create/serialize hybrid factories or all phases (and validate endstates)
        - create/serialize an openmmtools.integrators.PeriodicNonequilibriumIntegrator for all phases
        - relax generated structures with a minimizer and LangevinIntegrator for all phases
        - create/serialize a state associated with the relaxed structures
        - create/serialize a `core.xml` object for all phases

    arguments
        ligand_file : str
            .sdf (or any openeye-readable) file containing ligand labeled indices and structures
        old_ligand_index : int
            index of the old ligand
        new_ligand_index : int
            inded of the new ligand
        forcefield_files : list of str
            list of forcefields to use for complex/solvent parameterization
        trajectory_directory : str
            RUNXXX for FAH deployment
        complex_box_dimensions : Vec3, default=(9.8, 9.8, 9.8)
            define box dimensions of complex phase
        solvent_box_dimensions : Vec3, default=(3.5, 3.5, 3.5)
            define box dimensions of solvent phase
        timestep : simtk.unit.Quantity, default=4.*unit.femtosecond
            step size of nonequilibrium integration
        eq_splitting : str, default = 'V R O R V'
            splitting string of relaxation dynamics
        neq_splitting : str, default = 'V R H O R V'
            splitting string of nonequilibrium dynamics
        measure_shadow_work : bool, default=False
            True/False to measure shadow work
        pressure: float, default=1.
            pressure in atms for simulation
        temperature: float, default=300.,
            temperature in K for simulation
        phases: list, default = ['complex','solvent','vacuum']
            phases to run, where allowed phases are 'complex','solvent','vacuum'
        protein_pdb : str, default=None
            name of protein file
        receptor_mol2 : str, default=None
            name of receptor file if protein_pdb not provided
        small_molecule_forcefield : str, default='openff-1.0.0'
            small molecule forcefield filename
        small_molecule_parameters_cache : str, default=None
            cache file containing small molecule forcefield files
        atom_expression : list default=['IntType']
            list of string for atom mapping criteria. see oechem.OEExprOpts for options
        bond_expression : list default=['DefaultBonds']
            list of string for bond mapping criteria. see oechem.OEExprOpts for options
        map_strength : 'str', default=None
            atom and bond expressions will be ignored, and either a 'weak', 'default' or 'strong' map_strength will be used.
        spectators : str, default=None
            path to any non-alchemical atoms in simulation
        neglect_angles : bool, default=False
            wether to use angle terms in building of unique-new groups. False is strongly recommended
        anneal_14s : bool, default False
            Whether to anneal 1,4 interactions over the protocol;
        nonbonded_method : str, default='PME'
            nonbonded method to use
        softcore_v2=bool, default=False
            wether to use v2 softcore
        alchemical_functions : dict, default=DEFAULT_ALCHEMICAL_FUNCTIONS
            alchemical functions for transformation
        num_equilibration_iterations: int, default=1000
            number of equilibration steps to do during set up
        num_equilibration_steps_per_iteration: int, default=250,
            number of steps per iteration. default is 250 steps of 2fs, 1000 times which is 500ps of equilibration for SETUP
        nsteps_eq : int, default=250000
            number of normal MD steps to take for FAH integrator for PRODUCTION
        nsteps_neq : int, default=250000
            number of nonequilibrium steps to take for FAH integrator for PRODUCTION
        fe_type : str, default='fah'
            tells setup_relative_calculation() to use the fah pipeline
        collision_rate : simtk.unit.Quantity, default=1./unit.picosecond
            collision_rate for PRODUCTION
        collision_rate_setup : simtk.unit.Quantity, default=90./unit.picosecond
        constraint_tolerance : float, default=1e-6
            tolerance to use for constraints
        n_steps_per_move_application : int default=250
            number of equilibrium steps to take per move
    """
    from perses.app.setup_relative_calculation import run_setup
    from perses.utils import data
    #turn all of the args into a dict for passing to run_setup
    setup_options = locals()
    if 'kwargs' in setup_options.keys():
        setup_options.update(setup_options['kwargs'])

    #some modification for fah-specific functionality:
    setup_options['trajectory_prefix'] = None
    setup_options['anneal_1,4s'] = False
    from perses.utils.openeye import generate_expression
    setup_options['atom_expr'] = generate_expression(
        setup_options['atom_expression'])
    setup_options['bond_expr'] = generate_expression(
        setup_options['bond_expression'])

    #run the run_setup to generate topology proposals and htfs
    _logger.info(f"spectators: {setup_options['spectators']}")
    setup_dict = run_setup(setup_options,
                           serialize_systems=False,
                           build_samplers=False)
    topology_proposals = setup_dict['topology_proposals']
    htfs = setup_dict['hybrid_topology_factories']

    #create solvent and complex directories
    for phase in htfs.keys():
        _logger.info(f'PHASE RUNNING: {phase}')
        _logger.info(f'Setting up phase {phase}')
        if phase == 'solvent':
            phase_dir = f"{setup_options['solvent_projid']}/RUNS"
        if phase == 'complex':
            phase_dir = f"{setup_options['complex_projid']}/RUNS"
        if phase == 'vacuum':
            phase_dir = 'VACUUM/RUNS'
        dir = os.path.join(os.getcwd(), phase_dir, trajectory_directory)
        if not os.path.exists(dir):
            os.mkdir(dir)

        np.savez_compressed(f'{dir}/htf', htfs[phase])

        #serialize the hybrid_system
        data.serialize(htfs[phase].hybrid_system, f"{dir}/system.xml.bz2")

        #make and serialize an integrator
        integrator = make_neq_integrator(**setup_options)
        data.serialize(integrator, f"{dir}/integrator.xml")

        #create and serialize a state
        try:
            state = relax_structure(
                temperature=temperature,
                system=htfs[phase].hybrid_system,
                positions=htfs[phase].hybrid_positions,
                nequil=num_equilibration_iterations,
                n_steps_per_iteration=num_equilibration_steps_per_iteration,
                collision_rate=collision_rate_setup)

            data.serialize(state, f"{dir}/state.xml.bz2")
        except Exception as e:
            print(e)
            passed = False
        else:
            passed = True

        pos = state.getPositions(asNumpy=True)
        pos = np.asarray(pos)

        import mdtraj as md
        top = htfs[phase].hybrid_topology
        np.save(f'{dir}/hybrid_topology', top)
        traj = md.Trajectory(pos, top)
        traj.remove_solvent(exclude=['CL', 'NA'], inplace=True)
        traj.save(f'{dir}/hybrid_{phase}.pdb')

        #lastly, make a core.xml
        nsteps_per_cycle = 2 * nsteps_eq + 2 * nsteps_neq
        ncycles = 1
        nsteps_per_ps = 250
        core_parameters = {
            'numSteps': ncycles * nsteps_per_cycle,
            'xtcFreq': 1000 * nsteps_per_ps,  # once per ns
            'xtcAtoms': 'solute',
            'precision': 'mixed',
            'globalVarFilename': 'globals.csv',
            'globalVarFreq': 10 * nsteps_per_ps,
        }
        # Serialize core.xml
        import dicttoxml
        with open(f'{dir}/core.xml', 'wt') as outfile:
            #core_parameters = create_core_parameters(phase)
            xml = dicttoxml.dicttoxml(core_parameters,
                                      custom_root='config',
                                      attr_type=False)
            from xml.dom.minidom import parseString
            dom = parseString(xml)
            outfile.write(dom.toprettyxml())

        #create a logger for reference
        references = {
            'start_ligand': old_ligand_index,
            'end_ligand': new_ligand_index,
            'protein_pdb': protein_pdb,
            'passed_strucutre_relax': passed
        }

        np.save(f'{dir}/references', references)

        tp = topology_proposals
        from perses.utils.smallmolecules import render_atom_mapping
        render_atom_mapping(f'{dir}/atom_map.png', tp['ligand_oemol_old'],
                            tp['ligand_oemol_new'],
                            tp['non_offset_new_to_old_atom_map'])
Exemplo n.º 6
0
    def process(self, record, port):
        # Make sure we have a molecule defined
        if not record.has_value(OEPrimaryMolField()):
            record.set_value(self.args.log_field,
                             'Record is missing an input molecule field')
            self.failure.emit(record)
        mol = record.get_value(OEPrimaryMolField())

        # Report which compound we are processing
        from openeye.oechem import OEMolToSmiles
        smiles = OEMolToSmiles(mol)
        self.log.info(f"Processing compound {smiles}")

        # Generate arbitrary 3D coordinates for target ligand
        from openeye import oeomega
        omegaOpts = oeomega.OEOmegaOptions()
        omega = oeomega.OEOmega(omegaOpts)
        ret_code = omega.Build(mol)
        if ret_code != oeomega.OEOmegaReturnCode_Success:
            record.set_value(self.args.log_field,
                             oeomega.OEGetOmegaError(ret_code))
            self.failure.emit(record)

        from tempfile import TemporaryDirectory
        import os
        cwd = os.getcwd()
        with TemporaryDirectory() as tmpdir:
            self.log.info(f"Entering temporary directory {tmpdir}")
            os.chdir(tmpdir)

            # Set up perses calculation
            from perses.app.setup_relative_calculation import getSetupOptions, run_setup, run
            self.log.info(f"Loading setup options...")
            setup_options = getSetupOptions(self.yaml_filename)
            self.log.info(str(setup_options))

            # Prepare input for perses
            # TODO: Use tempdir in future for filesystem reasons
            self.log.info(f"Writing receptor...", flush=True)
            from openeye import oechem
            protein_pdb_filename = 'receptor.pdb'
            with oechem.oemolostream(protein_pdb_filename) as ofs:
                oechem.OEWriteMolecule(ofs, self._receptor)
            self.log.info(f"Writing ligands...", flush=True)
            ligands_sdf_filename = 'ligands.sdf'
            with oechem.oemolostream(ligands_sdf_filename) as ofs:
                oechem.OEWriteMolecule(ofs,
                                       self._reference_ligand)  # molecule 0
                oechem.OEWriteMolecule(ofs, mol)  # molecule 1

            self.log.info(f"Setting up perses calculation...", flush=True)
            perses_setup = run_setup(setup_options)

            self.log.info(f"Running calculations...", flush=True)
            run(self.yaml_filename)

            # Analyze the data
            self.log.info(f"Analyzing calculations...", flush=True)
            from perses.analysis.load_simulations import Simulation
            simulation = Simulation(0, 1)
            simulation.load_data()

            os.chdir(cwd)

        # Set output molecule information
        # TODO: Store trajectory or final snapshots
        phases = setup_options['phases']
        if ('complex' in phases) and ('solvent' in phases):
            self.log.info(
                f"DDG = {simulation.bindingdb} +- {simulation.bindingddg} kcal/mol..."
            )
            record.set_value(self.DDG_field, simulation.bindingdg)
            record.set_field(self.dDDG_field, simulation.bindingddg)
        elif 'vacuum' in setup_options['p']:
            self.log.info(
                f"DDG(vacuum) = {simulation.vacdb} +- {simulation.vacddg} kcal/mol..."
            )
            record.set_value(self.DDG_field, simulation.vacdg)
            record.set_field(self.dDDG_field, simulation.vacddg)
        self.success.emit(record)