Ejemplo n.º 1
0
Archivo: dsr.py Proyecto: dkratzert/DSR
class DSR(object):
    """
    main class
    """

    def __init__(self, options):
        """
        """
        import time
        time1 = time.clock()
        self.numpy_installed = False
        # options from the commandline options parser:
        self.options = options
        self.external = False
        self.fragment = ''
        self.helpmsg = "*** Please ask [email protected] for help ***"
        self.res_file = self.options.res_file
        if self.options.external_restr:
            self.external = True
            self.res_file = self.options.external_restr
        self.export_fragment = self.options.export_fragment
        if self.export_fragment:
            self.res_file = self.options.export_fragment
        if self.export_fragment:
            self.fragment = self.export_fragment
        self.export_clip = self.options.export_clip
        if self.export_clip:
            self.fragment = self.export_clip
        self.import_grade = self.options.import_grade
        self.export_all = self.options.export_all
        self.list_db = self.options.list_db
        self.list_db_csv = self.options.list_db_csv
        self.no_refine = self.options.no_refine
        self.invert = self.options.invert
        self.rigid = self.options.rigid_group
        self.search_string = self.options.search_string
        self.search_extern = self.options.search_extern
        self.head_csv = self.options.head_for_gui
        if self.head_csv:
            self.fragment = self.head_csv
        if self.options.selfupdate:
            import selfupdate
            selfupdate.update_dsr()
            sys.exit()
        #################################
        self.maindb_path = ''
        self.userdb_path = ''
        try:
            self.set_database_locations()
            self.gdb = ParseDB(self.maindb_path, self.userdb_path)
        except Exception as e:  # @UnusedVariable
            print("*** Initializing the database failed ***")
            raise
        #  List of database Fragments:
        if self.list_db_csv:
            print('DSR version: {}'.format(VERSION))
            for i in self.gdb.list_fragments():
                print('{};;{};;{};;{}'.format(i[0], i[3], i[1], i[2]))
            sys.exit()
        try:
            from export import Export
            self.export = Export(gdb=self.gdb, invert=self.invert)
        except Exception as e:
            print("*** Unable to export informations from DSR ***")
            print(e)
            sys.exit()
        #################################
        if self.head_csv:
            self.head_to_gui()
        if self.search_extern:
            result = search_fragment_name(self.search_extern, self.gdb, numresults=7)
            for i in result:
                print('{};;{};;{};;{}'.format(i[0], i[1], i[2], i[3]))
            sys.exit()
        print(program_name)
        if self.list_db:
            self.list_dbentries()
        if self.search_string:
            result = search_fragment_name(self.search_string, self.gdb, numresults=7)
            print_search_results(result)
            sys.exit()
        # Export !all! fragments
        if self.export_all:
            self.export.export_all_fragments()
        # Export one fragment
        if self.export_fragment:
            print('Exporting "{0}" to {0}.res'.format(self.fragment))
            self.fragment = self.export_fragment
            try:
                self.export.write_res_file(self.fragment)
            except:
                raise
            sys.exit()
        if self.export_clip:
            try:
                self.export.export_to_clip(self.fragment)
            except:
                raise
            sys.exit()
        # Import a GRADE fragment
        if self.import_grade:
            mog = ImportGRADE(self.import_grade, self.gdb, self.invert, self.gdb.maindb_path, self.gdb.userdb_path)
            mog.write_user_database()
            sys.exit()
        if not any(list(vars(self.options.all_options).values()) + [self.res_file]):
            self.options.error()
        if not self.res_file:
            self.options.error()
        self.rl = ResList(self.res_file)
        self.reslist = self.rl.get_res_list()
        try:
            import numpy as np
            self.numpy_installed = True
        except ImportError:
            print('*** Numpy was not found. Please reinstall DSR or install the numpy package in order to use DSR. ***')
            sys.exit()
        self.main()
        time2 = time.clock()
        runtime = (time2 - time1)
        print('Runtime: {:>.1f} s'.format(runtime))
        print('DSR run complete.')

    ###############################################################################

    def set_database_locations(self):
        """
        Tries to find the database files in their default locations after a regular DSR installation.
        Returns
        -------
        bool
        """
        if not self.userdb_path:
            # using the environment variable turned out to be too complicated.
            homedir = os.path.expanduser("~")
            self.userdb_path = os.path.join(homedir, "dsr_user_db.txt")
            if not os.path.isfile(self.userdb_path):
                touch(self.userdb_path)
        if not self.maindb_path:
            try:
                main_dbdir = os.environ["DSR_DIR"]
                self.maindb_path = os.path.join(main_dbdir, 'dsr_db.txt')
            except KeyError:
                main_dbdir = './'
                self.maindb_path = os.path.join(main_dbdir, 'dsr_db.txt')
        return True

    def head_to_gui(self):
        """
        Exports current fragment header and atoms to the GUI
        """
        atoms = []
        try:
            atoms = self.export.export_to_gui(self.fragment)
        except Exception as e:
            # print(e)
            print("*** Could not get atom information ***")
            print(self.helpmsg)
            sys.exit()
        print("\n<atoms>")
        print(atoms)
        print("</atoms>")
        # prints most of the needed info:
        self.gdb.get_head_for_gui(self.fragment)
        sys.exit()

    def list_dbentries(self):
        """
        list all entries in the db.
        """
        dbdir = os.path.expanduser('~')
        fragnames = []
        num = 0
        try:
            (width, _) = get_terminal_size()
        except():
            width = 80
        print('\n Entries found in the databases:\n')
        print(' Fragment         | Line | DB Name    | Full name, Comments ')
        print(sep_line)
        for num, line in enumerate(self.gdb.list_fragments()):
            fragnames.append(line[0])
            line = ' {:<17}| {:<5}| {:<11}| {}'.format(*line)
            print(line[:width - 1])
        print('\n {} Fragments in the database(s).'.format(num),
              '\n Feel free to add more fragments to "{}dsr_user_db.txt"'.format(dbdir + os.path.sep))
        for fragment in fragnames:
            self.gdb.check_consistency(fragment)
            self.gdb.check_db_atom_consistency(fragment)
            self.gdb.check_db_restraints_consistency(fragment)
            self.gdb.check_sadi_consistence(fragment)
        from selfupdate import is_update_needed
        if is_update_needed(silent=True):
            print("\n*** An update for DSR is available. You can update with 'dsr -u' ***")
        sys.exit()

    def main(self):
        """
        main object to run DSR as command line program
        """
        dbatoms = []
        # The database content:
        import atomhandling
        basefilename = filename_wo_ending(self.res_file)
        if not basefilename:
            print('*** Illegal option ***')
            sys.exit()
        if len(self.reslist) == 0:
            print("*** The input file is empty. Can not proceed! ***")
            sys.exit()
        find_atoms = atomhandling.FindAtoms(self.reslist)
        rle = ResListEdit(self.reslist, find_atoms)
        dsrp = DSRParser(self.reslist)
        self.fragment = dsrp.fragment
        restraints = self.gdb.get_restraints(self.fragment)  # this is only executed once
        db_residue_string = self.gdb.get_resi(self.fragment)
        dbatoms = self.gdb.get_atoms(self.fragment, self.invert)  # only the atoms of the dbentry as list
        # the atomtypes of the dbentry as list e.g. ['C', 'N', ...]
        db_atom_types = atomhandling.get_atomtypes(dbatoms)
        sf = atomhandling.SfacTable(self.reslist, db_atom_types)
        sfac_table = sf.set_sfac_table()  # from now on this sfac table is set
        resi = Resi(dsrp, db_residue_string, find_atoms)
        # line where the dsr command is found in the resfile:
        if dsrp.cf3_active:
            from cf3fit import CF3
            cf3 = CF3(rle, find_atoms, self.reslist, self.fragment, sfac_table,
                      basefilename, dsrp, resi, self.res_file, self.options)
            if self.fragment == 'cf3':
                cf3.cf3(afix='130')
            if self.fragment == 'cf6':
                cf3.cf3(afix='120')
            if self.fragment == 'cf9':
                cf3.cf9()
            print('\nFinished...')
            sys.exit()
        # checks have to be after CF3, CF6 etc.
        self.gdb.check_consistency(self.fragment)
        self.gdb.check_db_atom_consistency(self.fragment)
        self.gdb.check_db_restraints_consistency(self.fragment)
        self.gdb.check_sadi_consistence(self.fragment)
        if dsrp.occupancy:
            rle.set_free_variables(dsrp.occupancy)
        restraints = remove_resi(restraints)
        # corrects the atom type according to the previous defined global sfac table:
        dbatoms = atomhandling.set_final_db_sfac_types(db_atom_types, dbatoms, sfac_table)
        if not dsrp.unit_line:
            print('*** No UNIT instruction in res file found! Can not proceed! ***')
        print('Inserting {} into res File.'.format(self.fragment))
        if self.invert:
            print('Fragment inverted.')
        print('Source atoms: {}'.format(', '.join(dsrp.source)))
        print('Target atoms: {}'.format(', '.join(dsrp.target)))
        shx = ShelxlRefine(self.reslist, basefilename, find_atoms, self.options)
        shx.backup_shx_file()
        # several checks if the atoms in the dsr command line are consistent
        atomhandling.check_source_target(dsrp.source, dsrp.target, dbatoms)
        num = atomhandling.NumberScheme(self.reslist, dbatoms, dsrp)
        # returns also the atom names if residue is active
        fragment_numberscheme = num.get_fragment_number_scheme()
        print('Fragment atom names: {}'.format(', '.join(fragment_numberscheme)))
        dfix_head = ''
        if dsrp.dfix:
            restr = Restraints(self.fragment, self.gdb)
            dfix_12 = restr.get_formated_12_dfixes()
            dfix_13 = restr.get_formated_13_dfixes()
            flats = restr.get_formated_flats()
            restraints = dfix_12 + dfix_13 + flats
        # ##########Not using SHELXL for fragment fit: ###########

        print("--- Using fast fragment fit ---")
        if self.options.target_coords:
            target_coords = chunks(self.options.target_coords, 3)
        else:
            # {'C1': ['1.123', '0.7456', '3.245']}
            target_coordinates = find_atoms.get_atomcoordinates(dsrp.target)
            target_coords = [target_coordinates[key] for key in dsrp.target]
        # Uppercase is important here to avoid KeyErrors in source_atoms generation
        atnames = self.gdb.get_atomnames(self.fragment, uppercase=True)
        source_atoms = dict(zip(atnames, self.gdb.get_coordinates(self.fragment, cartesian=True, invert=self.invert)))
        # Coordinates only from the source, not the entire fragment:
        source_coords = [source_atoms[x] for x in dsrp.source]
        target_coords = [frac_to_cart(x, rle.get_cell()) for x in target_coords]
        from rmsd import fit_fragment
        # The source and target atom coordinates are fitted first. Then The complete fragment
        # is rotated and translated to the target position as calculated before.
        # parameter cartiesian has to be false here:
        fragment_coords = self.gdb.get_coordinates(self.fragment, cartesian=False, invert=self.invert)
        fitted_fragment, rmsd = fit_fragment(fragment_coords,
                                             source_atoms=source_coords,
                                             target_atoms=target_coords)
        # Moving back to the position of the first atom to have a reference:
        import numpy as np
        from rmsd import centroid
        # I have to make sure that I use the centroid of the correct atoms from target and source,
        # otherwise the fragment is shifted to a wrong position.
        # The third atom from the fragment e.g. has to be the third from the fragment to get
        # the correct centroid:
        center_difference = centroid(np.array(target_coords)) - \
                            centroid(np.array([list(fitted_fragment)[atnames.index(dsrp.source[x])]
                                               for x in range(len(source_coords))]))
        # finishing shift to correct centroid:
        fitted_fragment += center_difference
        # Or even lower than 0.1?
        if rmsd < 0.1:
            print('Fragment fit successful with RMSD of: {:8.3}'.format(rmsd))
        else:
            print('*** Fragment fit might have failed with RMSD of: {:8.3} ***'.format(rmsd))
        fitted_fragment = [cart_to_frac(x, rle.get_cell()) for x in fitted_fragment]
        afix_entry = []
        e2s = Elem_2_Sfac(sfac_table)
        for at, coord, atype in zip(fragment_numberscheme, fitted_fragment, db_atom_types):
            sfac_num = str(e2s.elem_2_sfac(atype))
            if dsrp.occupancy:
                occ = float(dsrp.occupancy)
            else:
                occ = 11.0
            afix_entry.append(isoatomstr.format(at, sfac_num, coord[0], coord[1], coord[2], occ, 0.03))
        afix_entry = "\n".join(afix_entry)
        new_atomnames = list(reversed(fragment_numberscheme))
        same_resi = ''
        if not dsrp.resiflag:
            restraints = rename_restraints_atoms(new_atomnames, self.gdb.get_atomnames(self.fragment), restraints)
        else:
            restraints = resi.format_restraints(restraints)
            # SADI\n
            same_resi = ["SAME_{} {} > {}\n".format(resi.get_residue_class, new_atomnames[-1], new_atomnames[0])]
        # Adds a "SAME_resiclass firstatom > lastatom" to the afix:
        if not self.options.rigid_group:
            restraints += same_resi
            # if dsrp.resiflag:  # <- Or should I do this?
            restraints += ["SIMU 0.04 0.08 1"]
        if not options.external_restr:
            restraints = remove_duplicate_restraints(self.reslist, restraints, resi.get_residue_class)
        restraints = wrap_headlines(restraints)
        dfx_file_name = ''
        if dsrp.part:
            afix_entry = "PART {}  {}\n".format(dsrp.part, dsrp.occupancy) + afix_entry + "\nPART 0"
        if dsrp.resiflag:
            afix_entry = 'RESI {} {}\n{}\nRESI 0'.format(resi.get_residue_class, resi.get_resinumber, afix_entry)
        if self.options.rigid_group:
            afix_entry = 'AFIX 9\n' + afix_entry
        if options.external_restr and not self.rigid:
            pname, ext = os.path.splitext(basefilename + '.dfix')
            if dsrp.dfix:
                dfx_file_name = pname + "_dfx" + ext
            else:
                dfx_file_name = pname + ext
            dfx_file_name = write_dbhead_to_file(dsrp, dfx_file_name, restraints, resi.get_residue_class,
                                                 resi.get_resinumber)
            if dsrp.resiflag:
                restraints = 'REM Restraints for residue {}:\n+{}\n' \
                    .format(resi.get_residue_class, dfx_file_name)
            else:
                restraints = 'REM Restraints for DSR fragment:\n+{}\n' \
                    .format(dfx_file_name)
        if self.options.rigid_group:
            afix_entry += '\nAFIX 0\n'

        # Adds the origin of restraints and fragment to res file:
        import textwrap
        source = textwrap.wrap("REM Restraints for Fragment {}, {} from: {}. "
                               "Please cite https://doi.org/10.1107/S1600576718004508".format(
            self.fragment,
            self.gdb.get_fragment_name(self.fragment),
            self.gdb.get_src(self.fragment)),
            width=74, subsequent_indent='REM ')
        source = '\n'.join(source) + '\n'
        # check if restraints already inserted:
        for line in self.reslist:
            try:
                if line.split()[4] == self.fragment + ',':
                    source = ''
                    break
            except IndexError:
                continue
        # + 'AFIX 0\n' before hklf seems to be not needed after shelx-2013:
        self.reslist[dsrp.hklf_line - 1] = self.reslist[dsrp.hklf_line - 1] + afix_entry + '\n'
        if not self.rigid:
            self.reslist[dsrp.unit_line] = self.reslist[dsrp.unit_line] + source + ''.join(restraints)

        # write to file:
        self.rl.write_resfile(self.reslist, '.res')
        if dsrp.command == 'REPLACE':
            print("Replace mode active\n")
            self.rl = ResList(self.res_file)
            reslist = self.rl.get_res_list()
            self.reslist, find_atoms = atomhandling.replace_after_fit(self.rl, reslist, resi,
                                                                      fragment_numberscheme, rle.get_cell())
            self.rl.write_resfile(self.reslist, '.res')
        os.remove(shx.backup_file)