Beispiel #1
0
def mate_genes(old_population, new_population, count, structure, site_list,
               backends, mutation_rate):
    """
    Make some new structures by mating current ones, based on position in
    the population.
    """
    while count > 0:
        index_0 = int(math.floor(len(old_population) * (random.random()**2)))
        index_1 = int(math.floor(len(old_population) * (random.random()**2)))
        old_gene_0 = old_population[index_0]['gene']
        old_gene_1 = old_population[index_1]['gene']
        debug("Mating {} and {}".format(old_gene_0, old_gene_1))
        new_gene = []
        for gene_part_0, gene_part_1 in zip(old_gene_0.split('.'),
                                            old_gene_1.split('.')):
            # Make a random split somewhere inside
            split = random.randint(0, len(gene_part_0))
            new_gene.append(
                mutate(gene_part_0[:split] + gene_part_1[split:],
                       mutation_rate))
        if any(x['gene'] == ".".join(new_gene) for x in new_population):
            # Alraedy in the population, don't include twice
            continue
        elif site_replace(structure,
                          replace_list=site_list,
                          manual_angles=new_gene,
                          backends=backends):
            debug("Successfully mated as {}".format(new_gene))
            individual = {'fitness': 0.0, 'gene': ".".join(new_gene)}
            new_population.append(individual)
            count -= 1
Beispiel #2
0
def fill_with_randoms(population, max_pop, structure, site_list, backends):
    """
    Make the population up to max_pop in size with completely random
    individuals.

    """
    while len(population) < max_pop:
        this_gene = []
        for functional_group, site in site_list:
            this_gene.append("".join(
                random.choice(string.ascii_lowercase)
                for _ in structure.attachments[site]))
        if site_replace(structure,
                        replace_list=site_list,
                        manual_angles=this_gene,
                        backends=backends):
            individual = {'fitness': 0.0, 'gene': ".".join(this_gene)}
            population.append(individual)
Beispiel #3
0
def fapswitch_deamon(structure, backends, rotations=12):
    """
    Use sockets to listen and receive structures.

    """
    timeout = options.getint('timeout')
    # set this to zero for random available port
    port = options.getint('port')
    listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # no host as this is running locally
    listener.bind(('', port))
    port = listener.getsockname()[1]

    # Ensure that we always know the port
    critical("Listening on port {} ...".format(port))

    listener.settimeout(timeout)
    listener.listen(1)
    # We only wait for a little bit so that process doesn't zombie forever
    try:
        conn, addr = listener.accept()
    except socket.timeout:
        error("No connection within {} seconds; exiting".format(timeout))
        listener.close()
        return False
    info('Connected by {}'.format(addr))

    # Server will continue until an empty input is sent or something times out
    conn.settimeout(timeout)
    while 1:
        try:
            line = conn.recv(1024).decode('utf-8')
        except socket.timeout:
            error(
                "Timed out after {} seconds waiting for input".format(timeout))
            return False

        # Empty input closes server
        if not line:
            break

        # collect information to send what has been done back
        processed = []

        # freeform strings are in braces {}, no spaces
        free_strings = re.findall('{(.*?)}', line)
        debug("Freeform strings: {}".format(free_strings))
        for free_string in free_strings:
            complete = freeform_replace(structure,
                                        custom=free_string,
                                        backends=backends,
                                        rotations=rotations)
            processed.append('{{{}}}'.format(free_string))
            processed.append('{}'.format(complete))

        # site replacements in square brackets [], no spaces
        site_strings = re.findall(r'\[(.*?)\]', line)
        debug("Site replacement strings: {}".format(site_strings))
        for site_string in site_strings:
            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("{}".format(site_list))
            debug("{}".format(manual_angles))
            complete = site_replace(structure,
                                    site_list,
                                    backends=backends,
                                    rotations=rotations,
                                    manual_angles=manual_angles)
            processed.append('[{}]'.format(site_string))
            processed.append('{}'.format(complete))

        try:
            conn.sendall((':'.join(processed)).encode('utf-8'))
        except conn.timeout:
            error("Timed out sending status after {} seconds".format(timeout))
            return False

    conn.close()
    return True
Beispiel #4
0
    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)
Beispiel #5
0
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))