Beispiel #1
0
 def test_mistaken_macro(self):
     swap_args = {"swap": ["VP"], "debug": True}
     bare_swap = AtomicSwapper([], maintain_num_species=False)
     bare_swap.periodic_table = get_periodic_table()
     bare_swap.swap_args = swap_args["swap"]
     # try to parse swaps
     bare_swap.parse_swaps()
     self.assertEqual(bare_swap.swap_pairs, [[["V"], ["P"]]])
Beispiel #2
0
    def __init__(
            self, cursor, swap=None, uniq=False, top=None, maintain_num_species=True, debug=False, **kwargs):
        """ Initialise class with query cursor and arguments.

        Parameters:
            cursor (list): cursor of documents to swap.

        Keyword arguments:
            swap (str): specification of swaps to perform, e.g.
                "LiP:KSn" will swap all Li->P and all K->Sn in the
                cursor.
            uniq (bool/float): filter documents by similarity with
                the default sim_tol (True) or the value provided here.
            top (int): only swap from the first `top` structures in
                the cursor.
            maintain_num_species (bool): only perform swaps that maintain
                the number of species in the structure
            debug (bool): enable debug output
            kwargs (dict): dictionary of extra arguments that should be ignored.

        """
        # define some swap macros
        self.periodic_table = get_periodic_table()
        self.maintain_num_species = maintain_num_species
        self.swap_dict_list = None
        self.swap_args = swap
        del self.periodic_table['X']
        self.template_structure = None
        self.cursor = list(cursor)
        if top is not None:
            self.cursor = self.cursor[:top]

        if len(self.cursor) == 0:
            return

        self.swap_counter = 0
        self.parse_swaps(self.swap_args)
        swap_cursor = []
        for doc in self.cursor:
            docs, counter = self.atomic_swaps(doc)
            self.swap_counter += counter
            if counter > 0:
                swap_cursor.extend(docs)
        self.cursor = swap_cursor
        if self.swap_counter > 0:
            print_success('Performed {} swaps.'.format(self.swap_counter))
        else:
            print_warning('No swaps performed.')

        if uniq:
            from matador.utils.cursor_utils import filter_unique_structures
            print('Filtering for unique structures...')
            filtered_cursor = filter_unique_structures(self.cursor, debug=debug, sim_tol=uniq)
            print('Filtered {} down to {}'.format(len(self.cursor), len(filtered_cursor)))
            self.cursor = filtered_cursor
Beispiel #3
0
 def test_null_self_swap(self):
     # spoof AtomicSwapper __init__
     swap_args = {"swap": ["KK:PP"], "debug": True}
     bare_swap = AtomicSwapper([])
     bare_swap.periodic_table = get_periodic_table()
     bare_swap.swap_args = swap_args["swap"]
     # try to parse swaps
     bare_swap.parse_swaps()
     self.assertEqual(bare_swap.swap_pairs, [[["K"], ["K"]], [["P"], ["P"]]])
     # set up test data for real swap
     doc = dict()
     doc["atom_types"] = ["K", "K", "P", "P"]
     swapped_docs, num_swapped = bare_swap.atomic_swaps(doc)
     self.assertEqual(num_swapped, 0)
Beispiel #4
0
 def test_maintain_num_species(self):
     # spoof AtomicSwapper __init__
     swap_args = {"swap": ["[Li,Na]K"], "debug": True}
     bare_swap = AtomicSwapper([])
     bare_swap.periodic_table = get_periodic_table()
     bare_swap.swap_args = swap_args["swap"]
     # try to parse swaps
     bare_swap.parse_swaps()
     # set up test data for real swap
     doc = dict()
     doc["atom_types"] = ["Li", "Na"]
     swapped_docs, num_swapped = bare_swap.atomic_swaps(doc)
     print(swapped_docs)
     self.assertEqual(num_swapped, 0)
Beispiel #5
0
 def test_many_to_one_macro(self):
     swap_args = {"swap": ["[V]P"], "debug": True}
     bare_swap = AtomicSwapper([], maintain_num_species=False)
     bare_swap.periodic_table = get_periodic_table()
     bare_swap.swap_args = swap_args["swap"]
     # try to parse swaps
     bare_swap.parse_swaps()
     self.assertEqual(bare_swap.swap_pairs, [[["N", "P", "As", "Sb", "Bi"], ["P"]]])
     # set up test data for real swap
     doc = dict()
     doc["atom_types"] = ["P", "Sb", "As", "As"]
     swapped_docs, num_swapped = bare_swap.atomic_swaps(doc)
     self.assertEqual(num_swapped, 1)
     self.assertEqual(swapped_docs[0]["atom_types"], ["P", "P", "P", "P"])
Beispiel #6
0
 def test_multiple_simple_swap(self):
     # spoof AtomicSwapper __init__
     swap_args = {"swap": ["AsP:LiNa"], "debug": True}
     bare_swap = AtomicSwapper([])
     bare_swap.periodic_table = get_periodic_table()
     bare_swap.swap_args = swap_args["swap"]
     # try to parse swaps
     bare_swap.parse_swaps()
     self.assertEqual(bare_swap.swap_pairs, [[["As"], ["P"]], [["Li"], ["Na"]]])
     # set up test data for real swap
     doc = dict()
     doc["atom_types"] = ["Li", "Li", "As", "As"]
     swapped_docs, num_swapped = bare_swap.atomic_swaps(doc)
     self.assertEqual(num_swapped, 1)
     self.assertEqual(swapped_docs[0]["atom_types"], ["Na", "Na", "P", "P"])
Beispiel #7
0
 def test_one_to_many_swap(self):
     # spoof AtomicSwapper __init__
     swap_args = {"swap": ["As[P,Sb,Zn,Cu]"], "debug": True}
     bare_swap = AtomicSwapper([])
     bare_swap.periodic_table = get_periodic_table()
     bare_swap.swap_args = swap_args["swap"]
     # try to parse swaps
     bare_swap.parse_swaps()
     self.assertEqual(bare_swap.swap_pairs, [[["As"], ["P", "Sb", "Zn", "Cu"]]])
     # set up test data for real swap
     doc = dict()
     doc["atom_types"] = ["Li", "Li", "As", "As"]
     swapped_docs, num_swapped = bare_swap.atomic_swaps(doc)
     self.assertEqual(num_swapped, 4)
     P_found = False
     Sb_found = False
     Zn_found = False
     Cu_found = False
     for new_doc in swapped_docs:
         self.assertTrue("As" not in new_doc["atom_types"])
         if "P" in new_doc["atom_types"]:
             self.assertTrue(
                 x not in new_doc["atom_types"] for x in ["Sb", "Zn", "Cu"]
             )
             self.assertEqual(new_doc["atom_types"], ["Li", "Li", "P", "P"])
             P_found = True
         if "Sb" in new_doc["atom_types"]:
             self.assertTrue(
                 x not in new_doc["atom_types"] for x in ["P", "Zn", "Cu"]
             )
             self.assertEqual(new_doc["atom_types"], ["Li", "Li", "Sb", "Sb"])
             Sb_found = True
         if "Zn" in new_doc["atom_types"]:
             self.assertTrue(
                 x not in new_doc["atom_types"] for x in ["P", "Sb", "Cu"]
             )
             self.assertEqual(new_doc["atom_types"], ["Li", "Li", "Zn", "Zn"])
             Zn_found = True
         if "Cu" in new_doc["atom_types"]:
             self.assertTrue(
                 x not in new_doc["atom_types"] for x in ["P", "Sb", "Zn"]
             )
             self.assertEqual(new_doc["atom_types"], ["Li", "Li", "Cu", "Cu"])
             Cu_found = True
     self.assertTrue(P_found)
     self.assertTrue(Sb_found)
     self.assertTrue(Zn_found)
     self.assertTrue(Cu_found)
Beispiel #8
0
 def test_multiple_many_to_one_swap(self):
     # spoof AtomicSwapper __init__
     swap_args = {"swap": ["[Li,Na]K:[Ru, Rh]La"], "debug": True}
     bare_swap = AtomicSwapper([], maintain_num_species=False)
     bare_swap.periodic_table = get_periodic_table()
     bare_swap.swap_args = swap_args["swap"]
     # try to parse swaps
     bare_swap.parse_swaps()
     self.assertEqual(
         bare_swap.swap_pairs, [[["Li", "Na"], ["K"]], [["Ru", "Rh"], ["La"]]]
     )
     # set up test data for real swap
     doc = dict()
     doc["atom_types"] = ["Li", "Na", "Ru", "Rh"]
     swapped_docs, num_swapped = bare_swap.atomic_swaps(doc)
     self.assertEqual(num_swapped, 1)
     self.assertEqual(swapped_docs[0]["atom_types"], ["K", "K", "La", "La"])
Beispiel #9
0
 def test_many_to_many_swap_awkward(self):
     # spoof AtomicSwapper __init__
     swap_args = {"swap": ["[Li,Na]K:[V,I][I]"], "debug": True}
     bare_swap = AtomicSwapper([], maintain_num_species=False)
     bare_swap.periodic_table = get_periodic_table()
     bare_swap.swap_args = swap_args["swap"]
     # try to parse swaps
     bare_swap.parse_swaps()
     self.assertEqual(
         bare_swap.swap_pairs,
         [[["Li", "Na"], ["K"]], [["V", "I"], ["Li", "Na", "K", "Rb", "Cs", "Fr"]]],
     )
     # set up test data for real swap
     doc = dict()
     doc["atom_types"] = ["Li", "Na", "V", "I"]
     swapped_docs, num_swapped = bare_swap.atomic_swaps(doc)
     self.assertEqual(num_swapped, 6)
     self.assertEqual(swapped_docs[0]["atom_types"], ["K", "K", "Li", "Li"])
Beispiel #10
0
    def __init__(
        self,
        client=False,
        collections=False,
        subcmd='query',
        debug=False,
        quiet=False,
        mongo_settings=None,
        **kwargs
    ):
        """ Parse arguments from matador or API call before calling
        query.

        Keyword arguments:
            client (pm.MongoClient): the MongoClient to connect to.
            collections (dict of pm.collections.Collection): dictionary of pymongo Collections.
            subcmd (str): either 'query' or 'hull', 'voltage', 'hulldiff'.
                These will decide whether calcuation accuracies are matched
                in the final results.

        """
        # read args and set housekeeping
        self.args = kwargs
        self.debug = debug
        if self.args.get('subcmd') is None:
            self.args['subcmd'] = subcmd
        if self.args.get('testing') is None:
            self.args['testing'] = False
        if self.args.get('as_crystal') is None:
            self.args['as_crystal'] = False

        if subcmd in ['hull', 'hulldiff', 'voltage'] and self.args.get('composition') is None:
            raise RuntimeError('{} requires composition query'.format(subcmd))

        self._create_hull = (self.args.get('subcmd') in ['hull', 'hulldiff', 'voltage'] or
                             self.args.get('hull_cutoff') is not None)

        # public attributes
        self.cursor = EmptyCursor()
        self.query_dict = None
        self.calc_dict = None
        self.repo = None

        # private attributes to be set later
        self._empty_query = None
        self._gs_enthalpy = None
        self._non_elemental = None
        self._chempots = None
        self._num_to_display = None

        if debug:
            print(self.args)

        if quiet:
            f = open(devnull, 'w')
            sys.stdout = f

        # if testing keyword is used, all database operations are ignored
        if not self.args.get('testing'):

            # connect to db or use passed client
            if client:
                self._client = client
                self._db = client.crystals
            if collections is not False:
                _collections = collections

            if (not collections or not client):
                # use passed settings or load from config file
                if mongo_settings:
                    self.mongo_settings = mongo_settings
                else:
                    self.mongo_settings = load_custom_settings(
                        config_fname=self.args.get('config'), debug=self.args.get('debug')
                    )

                result = make_connection_to_collection(
                    self.args.get('db'), mongo_settings=self.mongo_settings
                )
                # ideally this would be rewritten to use a context manager to ensure
                # that connections are _always_ cleaned up
                self._client, self._db, _collections = result

            if len(_collections) > 1:
                raise NotImplementedError("Querying multiple collections is no longer supported.")
            else:
                for collection in _collections:
                    self._collection = _collections[collection]
                    break

        # define some periodic table macros
        self._periodic_table = get_periodic_table()

        # set default top value to 10
        if self.args.get('summary') or self.args.get('subcmd') in ['swaps', 'polish']:
            self.top = None
        else:
            self.top = self.args.get('top') if self.args.get('top') is not None else 10

        # create the dictionary to pass to MongoDB
        self._construct_query()

        if not self.args.get('testing'):

            if self.args.get('id') is not None and (self._create_hull or self.args.get('calc_match')):
                # if we've requested and ID and hull/calc_match, do the ID query
                self.perform_id_query()

            self.perform_query()

            if self._create_hull and self.args.get('id') is None:
                # if we're making a normal hull, find the sets of calculations to use
                self.perform_hull_query()

            if not self._create_hull:
                # only filter for uniqueness if not eventually making a hull
                if self.args.get('uniq'):
                    from matador.utils.cursor_utils import filter_unique_structures
                    print_notify('Filtering for unique structures...')

                    if isinstance(self.cursor, pm.cursor.Cursor):
                        raise RuntimeError("Unable to filter pymongo cursor for uniqueness directly.")

                    if self.args.get('top') is not None:
                        top = self.args['top']
                    else:
                        top = len(self.cursor)

                    self.cursor = filter_unique_structures(
                        self.cursor[:top],
                        debug=self.args.get('debug'),
                        sim_tol=self.args.get('uniq'),
                        energy_tol=1e20
                    )

            if self.args.get('available_values') is not None:
                print('Querying available values...')
                self._query_available_values(self.args.get('available_values'), self.cursor)

            # if no client was passed, then we need to close the one we made
            if not client and not self.args.get('testing'):
                self._client.close()

        if quiet:
            f.close()
            sys.stdout = sys.__stdout__