def download_structure(inputpdbid): """Given a PDB ID, downloads the corresponding PDB structure. Checks for validity of ID and handles error while downloading. Returns the path of the downloaded file.""" try: if len(inputpdbid) != 4 or extract_pdbid(inputpdbid.lower()) == 'UnknownProtein': logger.error(f'invalid PDB-ID (wrong format): {inputpdbid}') sys.exit(1) pdbfile, pdbid = fetch_pdb(inputpdbid.lower()) pdbpath = tilde_expansion('%s/%s.pdb' % (config.BASEPATH.rstrip('/'), pdbid)) create_folder_if_not_exists(config.BASEPATH) with open(pdbpath, 'w') as g: g.write(pdbfile) logger.info(f'file downloaded as {pdbpath}') return pdbpath, pdbid except ValueError: # Invalid PDB ID, cannot fetch from RCBS server logger.error(f'PDB-ID does not exist: {inputpdbid}') sys.exit(1)
def main(): """Parse command line arguments and start main script for analysis.""" parser = ArgumentParser(prog="PLIP", description=description) pdbstructure = parser.add_mutually_exclusive_group( required=True) # Needs either PDB ID or file # '-' as file name reads from stdin pdbstructure.add_argument("-f", "--file", dest="input", nargs="+", help="Set input file, '-' reads from stdin") pdbstructure.add_argument("-i", "--input", dest="pdbid", nargs="+") outputgroup = parser.add_mutually_exclusive_group( required=False) # Needs either outpath or stdout outputgroup.add_argument("-o", "--out", dest="outpath", default="./") outputgroup.add_argument("-O", "--stdout", dest="stdout", action="store_true", default=False, help="Write to stdout instead of file") parser.add_argument("--rawstring", dest="use_raw_string", default=False, action="store_true", help="Use Python raw strings for stdin") parser.add_argument("-v", "--verbose", dest="verbose", default=False, help="Turn on verbose mode", action="store_true") parser.add_argument("-q", "--quiet", dest="quiet", default=False, help="Turn on quiet mode", action="store_true") parser.add_argument("-s", "--silent", dest="silent", default=False, help="Turn on silent mode", action="store_true") parser.add_argument("-p", "--pics", dest="pics", default=False, help="Additional pictures", action="store_true") parser.add_argument("-x", "--xml", dest="xml", default=False, help="Generate report file in XML format", action="store_true") parser.add_argument("-t", "--txt", dest="txt", default=False, help="Generate report file in TXT (RST) format", action="store_true") parser.add_argument("-y", "--pymol", dest="pymol", default=False, help="Additional PyMOL session files", action="store_true") parser.add_argument( "--maxthreads", dest="maxthreads", default=multiprocessing.cpu_count(), help= "Set maximum number of main threads (number of binding sites processed simultaneously)." "If not set, PLIP uses all available CPUs if possible.", type=int) parser.add_argument( "--breakcomposite", dest="breakcomposite", default=False, help= "Don't combine ligand fragments with covalent bonds but treat them as single ligands for the analysis.", action="store_true") parser.add_argument( "--altlocation", dest="altlocation", default=False, help= "Also consider alternate locations for atoms (e.g. alternate conformations).", action="store_true") parser.add_argument("--nofix", dest="nofix", default=False, help="Turns off fixing of PDB files.", action="store_true") parser.add_argument("--nofixfile", dest="nofixfile", default=False, help="Turns off writing files for fixed PDB files.", action="store_true") parser.add_argument( "--nopdbcanmap", dest="nopdbcanmap", default=False, help= "Turns off calculation of mapping between canonical and PDB atom order for ligands.", action="store_true") parser.add_argument( "--dnareceptor", dest="dnareceptor", default=False, help= "Treat nucleic acids as part of the receptor structure (together with any present protein) instead of as a ligand.", action="store_true") parser.add_argument( "--name", dest="outputfilename", default="report", help= "Set a filename for the report TXT and XML files. Will only work when processing single structures." ) ligandtype = parser.add_mutually_exclusive_group( ) # Either peptide/inter or intra mode ligandtype.add_argument( "--peptides", "--inter", dest="peptides", default=[], help= "Allows to define one or multiple chains as peptide ligands or to detect inter-chain contacts", nargs="+") ligandtype.add_argument( "--intra", dest="intra", help="Allows to define one chain to analyze intra-chain contacts.") parser.add_argument("--keepmod", dest="keepmod", default=False, help="Keep modified residues as ligands", action="store_true") parser.add_argument( "--nohydro", dest="nohydro", default=False, help= "Do not add polar hydrogens in case your structure already contains hydrogens.", action="store_true") parser.add_argument( "--model", dest="model", default=1, type=int, help="Model number to be used for multi-model structures.") # Optional threshold arguments, not shown in help thr = namedtuple('threshold', 'name type') thresholds = [ thr(name='aromatic_planarity', type='angle'), thr(name='hydroph_dist_max', type='distance'), thr(name='hbond_dist_max', type='distance'), thr(name='hbond_don_angle_min', type='angle'), thr(name='pistack_dist_max', type='distance'), thr(name='pistack_ang_dev', type='other'), thr(name='pistack_offset_max', type='distance'), thr(name='pication_dist_max', type='distance'), thr(name='saltbridge_dist_max', type='distance'), thr(name='halogen_dist_max', type='distance'), thr(name='halogen_acc_angle', type='angle'), thr(name='halogen_don_angle', type='angle'), thr(name='halogen_angle_dev', type='other'), thr(name='water_bridge_mindist', type='distance'), thr(name='water_bridge_maxdist', type='distance'), thr(name='water_bridge_omega_min', type='angle'), thr(name='water_bridge_omega_max', type='angle'), thr(name='water_bridge_theta_min', type='angle') ] for t in thresholds: parser.add_argument('--%s' % t.name, dest=t.name, type=lambda val: threshold_limiter(parser, val), help=argparse.SUPPRESS) arguments = parser.parse_args() # configure log levels config.VERBOSE = True if arguments.verbose else False config.QUIET = True if arguments.quiet else False config.SILENT = True if arguments.silent else False if config.VERBOSE: logger.setLevel(logging.DEBUG) elif config.QUIET: logger.setLevel(logging.WARN) elif config.SILENT: logger.setLevel(logging.CRITICAL) else: logger.setLevel(config.DEFAULT_LOG_LEVEL) config.MAXTHREADS = arguments.maxthreads config.XML = arguments.xml config.TXT = arguments.txt config.PICS = arguments.pics config.PYMOL = arguments.pymol config.STDOUT = arguments.stdout config.RAWSTRING = arguments.use_raw_string config.OUTPATH = arguments.outpath config.OUTPATH = tilde_expansion( "".join([config.OUTPATH, '/'] ) if not config.OUTPATH.endswith('/') else config.OUTPATH) config.BASEPATH = config.OUTPATH # Used for batch processing config.BREAKCOMPOSITE = arguments.breakcomposite config.ALTLOC = arguments.altlocation config.PEPTIDES = arguments.peptides config.INTRA = arguments.intra config.NOFIX = arguments.nofix config.NOFIXFILE = arguments.nofixfile config.NOPDBCANMAP = arguments.nopdbcanmap config.KEEPMOD = arguments.keepmod config.DNARECEPTOR = arguments.dnareceptor config.OUTPUTFILENAME = arguments.outputfilename config.NOHYDRO = arguments.nohydro config.MODEL = arguments.model # Make sure we have pymol with --pics and --pymol if config.PICS or config.PYMOL: try: import pymol except ImportError: logger.error('PyMOL is required for the --pics and --pymol option') sys.exit(1) # Assign values to global thresholds for t in thresholds: tvalue = getattr(arguments, t.name) if tvalue is not None: if t.type == 'angle' and not 0 < tvalue < 180: # Check value for angle thresholds parser.error( "Threshold for angles need to have values within 0 and 180." ) if t.type == 'distance': if tvalue > 10: # Check value for angle thresholds parser.error( "Threshold for distances must not be larger than 10 Angstrom." ) elif tvalue > config.BS_DIST + 1: # Dynamically adapt the search space for binding site residues config.BS_DIST = tvalue + 1 setattr(config, t.name.upper(), tvalue) # Check additional conditions for interdependent thresholds if not config.HALOGEN_ACC_ANGLE > config.HALOGEN_ANGLE_DEV: parser.error( "The halogen acceptor angle has to be larger than the halogen angle deviation." ) if not config.HALOGEN_DON_ANGLE > config.HALOGEN_ANGLE_DEV: parser.error( "The halogen donor angle has to be larger than the halogen angle deviation." ) if not config.WATER_BRIDGE_MINDIST < config.WATER_BRIDGE_MAXDIST: parser.error( "The water bridge minimum distance has to be smaller than the water bridge maximum distance." ) if not config.WATER_BRIDGE_OMEGA_MIN < config.WATER_BRIDGE_OMEGA_MAX: parser.error( "The water bridge omega minimum angle has to be smaller than the water bridge omega maximum angle" ) expanded_path = tilde_expansion( arguments.input) if arguments.input is not None else None run_analysis(expanded_path, arguments.pdbid) # Start main script