def main(): """ Main logic to find lowest energy conformation of the functional groups through an evolutionary. """ info("Welcome to confswitch; finding your lowest energy conformers") info("Using fapswitch version {}".format(fapswitch.__version__)) # Name for a the single structure job_name = options.get('job_name') # Load it input_structure = load_structure(job_name) # Structure is ready! # Begin processing info("Structure attachment sites: " "{}".format(list(input_structure.attachments))) info("Structure attachment multiplicities: " "{}".format( dict((key, len(val)) for key, val in input_structure.attachments.items()))) # Functional group library is self initialising info("Groups in library: {}".format(functional_groups.group_list)) # Only use the cif backend here backends = [CifFileBackend()] # Optimise each custom string passed to fapswitch custom_strings = options.get('custom_strings') site_strings = re.findall(r'\[(.*?)\]', custom_strings) debug("Site replacement options strings: {}".format(site_strings)) for site_string in site_strings: # These should be [email protected]_group2@site2 # with optional %angle site_list = [] manual_angles = [] for site in [x for x in site_string.split('.') if x]: site_id, functionalisation = site.split('@') if '%' in functionalisation: functionalisation, manual = functionalisation.split('%') else: manual = None site_list.append([site_id, functionalisation]) manual_angles.append(manual) debug(str(site_list)) debug(str(manual_angles)) optimise_conformation(input_structure, site_list, backends=backends)
def main(): """ Initialise everything needed to get the daemon running and start it up. """ info("Welcome to fapswitchd; the daemon interface to fapswitch") info("Using fapswitch version {}".format(fapswitch.__version__)) # Name for a the single structure job_name = options.get('job_name') # Load it input_structure = load_structure(job_name) # Structure is ready! # Begin processing info("Structure attachment sites: " "{}".format(list(input_structure.attachments))) info("Structure attachment multiplicities :" "{}".format( dict((key, len(val)) for key, val in input_structure.attachments.items()))) # Functional group library is self initialising info("Groups in library: {}".format(functional_groups.group_list)) #Define some backends for where to send the structures backends = [] backend_options = options.gettuple('backends') rotations = options.getint('rotations') info("Will rotate each group a maximum of {} times.".format(rotations)) if 'sqlite' in backend_options: # Initialise and add the database writer debug("Initialising the sqlite backend") try: from fapswitch.backend.sql import AlchemyBackend backend = AlchemyBackend(job_name) backend.populate_groups(functional_groups) backends.append(backend) except ImportError: error("SQLAlchemy not installed; sql backend unavailable") # done if 'file' in backend_options: # Just dumps to a named file debug("Initialising cif file writer backend") from fapswitch.backend.cif_file import CifFileBackend backends.append(CifFileBackend()) # Make the program die if the daemon is called unsuccessfully if fapswitch_deamon(input_structure, backends=backends, rotations=rotations): info("Daemon completed successfully") else: error("Daemon did not complete successfully; check output")
distance = distance + pow(p - q, 2); }; } else { distance = 0; for(i=0; i<3; i++) { p = c_cob(i); q = c_coa(i); distance = distance + pow(p - q, 2); }; }; return_val = sqrt(distance); """ return weave.inline(code, ['c_coa', 'f_coa', 'c_cob', 'f_cob', 'box'], type_converters=converters.blitz, support_code='#include <math.h>') # Make an importable tester that picks up values from the global # options set _test_method = options.get('collision_method') _test_scale = options.getfloat('collision_scale') test_collision = make_collision_tester(_test_method, _test_scale)
def handle_request(self): """Generate a random structure and return the rendered page.""" debug("Arguments: {}".format(self.request.arguments)) max_trials = 20 top_50_groups = [ "Me", "Ph", "Cl", "OMe", "OH", "Et", "OEt", "F", "Br", "NO2", "NH2", "CN", "COOEt", "COMe", "COOH", "Bnz", "COOMe", "iPr", "pTol", "4ClPh", "tBu", "4OMePh", "CF3", "COPh", "Pr", "NMe2", "Bu", "OBnz", "4NO2Ph", "OAc", "4FPh", "I", "4BrPh", "2ClPh", "All", "COH", "SMe", "CONH2", "NPh", "24DClPh", "CHex", "Morph", "HCO", "3ClPh", "oTol", "2Fur", "iBu", "NCOMe" ] small_groups = ["F", "Cl", "Me", "NH2", "OH", "CN"] # Possible options: # replace_only: tuple of sites to replace # groups_only: only use specific groups # max_different: restrict simultaneous types of groups # This is new every time and keeps all the information # we need specific to the web version backends = [WebStoreBackend()] failed = "" if 'mof-choice' in self.request.arguments: # Selected a specific MOF chosen_structure = self.get_argument('mof-choice') base_structure = get_structure(chosen_structure) replace_list = [] for site in available_structures[chosen_structure]: group = self.get_argument(site, None) if group is None or 'None' in group: continue elif 'Random' in group: replace_list.append([random.choice(top_50_groups), site]) else: replace_list.append([group, site]) # Now make the MOF status = site_replace(base_structure, replace_list=replace_list, backends=backends) if not status: # couldn't make it so just use clean structure failed = ".".join("{}@{}".format(x[0], x[1]) for x in replace_list) site_replace(base_structure, replace_list=[], backends=backends) else: # Completely random chosen_structure = random.choice(list(available_structures)) # Make sure we have functionalisation sites while len(available_structures[chosen_structure]) == 0: chosen_structure = random.choice(list(available_structures)) # Here's the actual structure base_structure = get_structure(chosen_structure) # Use several combinations to try to get something functionalised trial_number = 0 while trial_number < max_trials: if trial_number < max_trials / 4.0: debug("Trial all groups: {}".format(trial_number)) status = random_combination_replace( structure=base_structure, backends=backends, max_different=2) elif trial_number < 2.0 * max_trials / 4.0: debug("Trial max one group: {}".format(trial_number)) status = random_combination_replace( structure=base_structure, backends=backends, max_different=1) elif trial_number < 3.0 * max_trials / 4.0: debug("Trial top 50: {}".format(trial_number)) status = random_combination_replace( structure=base_structure, backends=backends, groups_only=top_50_groups, max_different=2) else: debug("Trial small groups: {}".format(trial_number)) status = random_combination_replace( structure=base_structure, backends=backends, groups_only=small_groups, max_different=1) # If functionalisation attempted if status: if backends[0].cifs[-1]['functions']: # it was successful; done here break else: # only increment if we actually tried to add groups trial_number += 1 else: site_replace(base_structure, replace_list=[], backends=backends) failed = "{} random combinations".format(max_trials) # Should always have a structure, even if it is clean; but failed will # be True for that cif_info = backends[0].cifs[-1] # MEPO compatibility if all groups are okay if all(functional_groups[function[0]].mepo_compatible for function in cif_info['functions']): mepo_compatible = "Yes" else: mepo_compatible = "No" collision_tester = options.get('collision_method') collision_cutoff = options.getfloat('collision_scale') if cif_info['ligands'] is None: ligands = [] else: ligands = cif_info['ligands'] if ligands: sa_score = max(ligand.sa_score for ligand in ligands) else: sa_score = 0.0 processed_ligands = make_ligands(ligands) extra_info = """<h4>Hypothetical functionalised MOF</h4> <p>Functional groups have been added using the crystal symmetry. A collision detection routine with a {} radius at {:.2f} was used to carry out the functionalisation. Note that although atoms may appear close, the bonding connectivity defined in the cif file will be correct.</p> """.format(collision_tester, collision_cutoff) # These references are always required local_references = [ references['Kadantsev2013'], references['Ertl2009'], references['Chung2014'] ] # Find all the references and add them too for reference in re.findall(r'\[(.*?)\]', extra_info): local_references.append(references[reference]) # Raw HTML anchors. Ugly. extra_info = re.sub( r'\[(.*?)\]', # non-greedy(?) find in square brackets r'[<a href="#\1">\1</a>]', # replace raw html extra_info) page = templates.load('random.html').generate( mepo_compatible=mepo_compatible, references=local_references, functional_groups=functional_groups, extra_info=extra_info, sa_score=sa_score, processed_ligands=processed_ligands, available_structures=available_structures, failed=failed, **cif_info) self.write(page)
def main(): """ Simple application that will read options and run the substitution for an input structure. """ info("Welcome to cliswitch; the command line interface to fapswitch") info("Using fapswitch version {}".format(fapswitch.__version__)) # Name for a the single structure job_name = options.get('job_name') # Load it input_structure = load_structure(job_name) # Structure is ready! # Begin processing info("Structure attachment sites: " "{}".format(list(input_structure.attachments))) info("Structure attachment multiplicities: " "{}".format(dict((key, len(val)) for key, val in input_structure.attachments.items()))) # Will use selected sites if specified, otherwise use all replace_only = options.gettuple('replace_only') if replace_only == (): replace_only = None # Functional group library is self initialising info("Groups in library: {}".format(functional_groups.group_list)) #Define some backends for where to send the structures backends = [] backend_options = options.gettuple('backends') if 'sqlite' in backend_options: # Initialise and add the database writer debug("Initialising the sqlite backend") try: from fapswitch.backend.sql import AlchemyBackend backend = AlchemyBackend(job_name) backend.populate_groups(functional_groups) backends.append(backend) except ImportError: error("SQLAlchemy not installed; sql backend unavailable") # done if 'file' in backend_options: # Just dumps to a named file debug("Initialising cif file writer backend") from fapswitch.backend.cif_file import CifFileBackend backends.append(CifFileBackend()) ## # User defined, single-shot functionalisations ## rotations = options.getint('rotations') info("Will rotate each group a maximum of {} times.".format(rotations)) custom_strings = options.get('custom_strings') # Pattern matching same as in the daemon # freeform strings are in braces {}, no spaces freeform_strings = re.findall('{(.*?)}', custom_strings) debug("Freeform option strings: {}".format(freeform_strings)) for freeform_string in freeform_strings: freeform_replace(input_structure, custom=freeform_string, backends=backends, rotations=rotations) # site replacements in square brackets [], no spaces site_strings = re.findall(r'\[(.*?)\]', custom_strings) debug("Site replacement options strings: {}".format(site_strings)) for site_string in site_strings: # These should be [email protected]_group2@site2 # with optional %angle site_list = [] manual_angles = [] for site in [x for x in site_string.split('.') if x]: site_id, functionalisation = site.split('@') if '%' in functionalisation: functionalisation, manual = functionalisation.split('%') else: manual = None site_list.append([site_id, functionalisation]) manual_angles.append(manual) debug(str(site_list)) debug(str(manual_angles)) site_replace(input_structure, site_list, backends=backends, rotations=rotations, manual_angles=manual_angles) ## # Full systematic replacement of everything start here ## # Only use these functional groups for replacements replace_groups = options.gettuple('replace_groups') if replace_groups == (): replace_groups = None max_different = options.getint('max_different') prob_unfunc = options.getfloat('unfunctionalised_probability') # Do absolutely every combination (might take a while) if options.getbool('replace_all_sites'): all_combinations_replace(input_structure, replace_only=replace_only, groups_only=replace_groups, max_different=max_different, backends=backends, rotations=rotations) # group@site randomisations random_count = options.getint('site_random_count') successful_randoms = 0 while successful_randoms < random_count: #function returns true if structure is generated if random_combination_replace(input_structure, replace_only=replace_only, groups_only=replace_groups, max_different=max_different, prob_unfunc=prob_unfunc, backends=backends, rotations=rotations): successful_randoms += 1 info("Generated %i of %i site random structures" % (successful_randoms, random_count)) # fully freeform randomisations random_count = options.getint('full_random_count') successful_randoms = 0 while successful_randoms < random_count: #function returns true if structure is generated if freeform_replace(input_structure, replace_only=replace_only, groups_only=replace_groups, max_different=max_different, prob_unfunc=prob_unfunc, backends=backends, rotations=rotations): successful_randoms += 1 info("Generated %i of %i fully random structures" % (successful_randoms, random_count))
def load_structure(name): """ Load a structure from a pickle or generate a new one as required. Returns an initialised structure. Caches loaded structure on disk. """ info("Structure version {}.{}".format(*DOT_FAPSWITCH_VERSION)) pickle_file = "__{}.fapswitch".format(name) loaded = False if path.exists(pickle_file): info("Existing structure found: {}; loading...".format(pickle_file)) #TODO(tdaff): deal with errors try: with open(pickle_file, 'rb') as p_structure: structure = pickle.load(p_structure) # Negative versions ensure that very old caches will be removed if not hasattr(structure, 'fapswitch_version'): structure.fapswitch_version = (-1, -1) # Need to make sure it is still valid if structure.fapswitch_version[0] < DOT_FAPSWITCH_VERSION[0]: error("Old dot-fapswitch detected, re-initialising") loaded = False elif structure.fapswitch_version[1] < DOT_FAPSWITCH_VERSION[1]: warning( "Cached file {} may be out of date".format(pickle_file)) loaded = True else: debug("Finished loading") loaded = True except EOFError: warning("Corrupt pickle; re-initialising") loaded = False if not loaded: info("Initialising a new structure. This may take some time.") structure = Structure(name) structure.from_cif('{}.cif'.format(name)) # Ensure that atoms in the structure are properly typed structure.gen_factional_positions() bonding_src = options.get('connectivity') if bonding_src == 'file': # Rudimentary checks for poor structures if not hasattr(structure, 'bonds'): error("No bonding in input structure, will probably fail") elif len(structure.bonds) == 0: error("Zero bonds found, will fail") elif not hasattr(structure.atoms[0], 'uff_type'): warning("Atoms not properly typed, expect errors") else: info("Bonding from input file used") elif bonding_src == 'bondsonly': info("Generating atom types from cif bonding") structure.gen_types_from_bonds() elif bonding_src == 'openbabel': info("Generating topology with Open Babel") structure.gen_babel_uff_properties() # A couple of structure checks structure.check_close_contacts() structure.bond_length_check() # Initialise the sites after bonds are perceived structure.gen_attachment_sites() structure.gen_normals() # Cache the results info("Dumping cache of structure to {}".format(pickle_file)) structure.fapswitch_version = DOT_FAPSWITCH_VERSION with open(pickle_file, 'wb') as p_structure: pickle.dump(structure, p_structure, protocol=-1) return structure
def __init__(self): self.hash_filenames = options.get('hash_filenames').lower()