def create_hull(self): """ Begin the hull creation routines and perform the post-processing specified by initial arguments. """ if self.args.get('uniq'): from matador.utils.cursor_utils import filter_unique_structures if self.args.get('uniq') is True: sim_tol = 0.1 else: sim_tol = self.args.get('uniq') print_notify('Filtering for unique structures...') self.cursor = filter_unique_structures(self.cursor, args=self.args, quiet=True, sim_tol=sim_tol, hull=True, energy_key=self.energy_key) self.construct_phase_diagram() if not self.hull_cursor: print_warning( 'No structures on hull with chosen chemical potentials.') else: print_notify( '{} structures found within {} eV of the hull, including chemical potentials.' .format(len(self.hull_cursor), self.hull_cutoff)) display_results(self.hull_cursor, hull=True, energy_key=self.energy_key, **self.args) if self.compute_voltages: print("Constructing electrode system with active ion: {}".format( self.species[0])) self.voltage_curve() if self.compute_volumes: self.volume_curve() if not self.args.get('no_plot'): if self.compute_voltages and self.voltage_data: self.plot_voltage_curve(show=False) if self.compute_volumes and self.volume_data: self.plot_volume_curve(show=False) self.plot_hull(**self.args['plot_kwargs'], debug=self.args.get('debug'), show=True)
def print_diff(self, **args): """ Use the display_results function to print the diff. """ display_results(self.cursor, hull=True, additions=self.additions, deletions=self.deletions) if self.additions is not None: if args.get('summary'): print( '{} additions and {} replacements as best at stoichiometry.' .format(len(self.additions), len(self.deletions))) else: print('{} additions and {} deletions.'.format( len(self.additions), len(self.deletions)))
def __str__(self): """ Print underlying phase diagram. """ return display_results(self.cursor, hull=True, colour=False, energy_key=self.formation_key, sort=False, return_str=True)
def perform_id_query(self): """ Query the `text_id` field for the ID provided in the args for a calc_match or hull/voltage query. Use the results of the text_id query to match to other entries that have the same calculation parameters. Sets self.query_dict and self.calc_dict. Raises: RuntimeError: if no structures are found. """ self.cursor = [] query_dict = dict() query_dict['$and'] = [] query_dict['$and'].append(self._query_id()) if not self.args.get('ignore_warnings'): query_dict['$and'].append(self._query_quality()) self.repo = self._collection self.cursor = list(self._find_and_sort(query_dict)) if not self.cursor: raise RuntimeError('Could not find a match with {} try widening your search.'.format(self.args.get('id'))) if len(self.cursor) >= 1: display_results(list(self.cursor)[:self.top], **self.args) if len(self.cursor) > 1: print_warning('Matched multiple structures with same text_id. The first one will be used.') # save special copy of calc_dict for hulls self.calc_dict = dict() self.calc_dict['$and'] = [] # to avoid deep recursion, and since this is always called first # don't append, just set self.query_dict = self._query_calc(self.cursor[0]) if self.args.get('composition'): self.args['intersection'] = True self.query_dict['$and'].append(self._query_composition()) self.calc_dict['$and'] = list(self.query_dict['$and'])
def perform_query(self): """ Find results that match the query_dict inside the MongoDB database. """ # if no query submitted, find all if self._empty_query and self.args.get('id') is None: self.repo = self._collection self.cursor, cursor_count = self._perform_empty_query() # if no special query has been made already, begin executing the query if not self._empty_query: self.repo = self._collection if self.debug: print('Query dict:') print(dumps(self.query_dict, indent=1)) # execute query self.cursor, cursor_count = self._find_and_sort(self.query_dict) if self._non_elemental: self.cursor = filter_cursor_by_chempots(self._chempots, self.cursor) print('{} results found for query in {}.'.format(cursor_count, self.repo.name)) self._num_to_display = cursor_count if self.args.get('subcmd') != 'swaps' and not self._create_hull: self._set_filter_display_results(cursor_count) # if a summary has been requested, cursor must be converted to list if self.args.get('summary'): self.cursor = list(self.cursor) if self.args.get('subcmd') != 'swaps' and not self._create_hull: if self._num_to_display >= 1 or self._num_to_display is None: if self._num_to_display == cursor_count: display_results(self.cursor, **self.args) else: display_results(self.cursor[:self._num_to_display], **self.args) if isinstance(self.cursor, pm.cursor.Cursor): self.cursor.rewind()
def query2files(cursor, dirname=None, max_files=10000, top=None, prefix=None, cell=None, param=None, res=None, pdb=None, json=None, xsf=None, markdown=True, latex=False, subcmd=None, argstr=None, **kwargs): """ Many-to-many convenience function for many structures being written to many file types. Parameters: cursor (:obj:`list` of :obj:`dict`/:class:`AtomicSwapper`): list of matador dictionaries to write out. Keyword arguments: dirname (str): the folder to save the results into. Will be created if non-existent. Will have integer appended to it if already existing. max_files (int): if the number of files to be written exceeds this number, then raise RuntimeError. **kwargs (dict): dictionary of {filetype: bool(whether to write)}. Accepted file types are cell, param, res, pdb, json, xsf, markdown and latex. """ multiple_files = any((cell, param, res, pdb, xsf)) prefix = prefix + '-' if prefix is not None else '' if isinstance(cursor, AtomicSwapper): cursor = cursor.cursor subcmd = "swaps" if subcmd in ['polish', 'swaps']: info = False hash_dupe = False else: info = True hash_dupe = False if isinstance(cursor, list): num = len(cursor) else: num = cursor.count() if top is not None: if top < num: num = top num_files = num * sum(1 for ext in [cell, param, res, pdb, xsf] if ext) if multiple_files: print('Intending to write', num, 'structures to file...') if num_files > max_files: raise RuntimeError( "Not writing {} files as it exceeds argument `max_files` limit of {}" .format(num_files, max_files)) if dirname is None: dirname = generate_relevant_path(subcmd=subcmd, **kwargs) _dir = False dir_counter = 0 # postfix integer on end of directory name if it exists while not _dir: if dir_counter != 0: directory = dirname + str(dir_counter) else: directory = dirname if not os.path.isdir(directory): os.makedirs(directory) _dir = True else: dir_counter += 1 for _, doc in enumerate(cursor[:num]): # generate an appropriate filename for the structure root_source = get_root_source(doc) if '_swapped_stoichiometry' in doc: formula = get_formula_from_stoich(doc['_swapped_stoichiometry']) else: formula = get_formula_from_stoich(doc['stoichiometry']) if subcmd == 'swaps': root_source = root_source.replace('-swap-', '-') name = root_source if 'OQMD ' in root_source: name = '{formula}-OQMD_{src}'.format( formula=formula, src=root_source.split(' ')[-1]) elif 'mp-' in root_source: name = '{formula}-MP_{src}'.format(formula=formula, src=root_source.split('-')[-1]) if 'icsd' in doc and 'CollCode' not in name: name += '-CollCode{}'.format(doc['icsd']) else: pf_id = None for source in doc['source']: if 'pf-' in source: pf_id = source.split('-')[-1] break else: if 'pf_ids' in doc: pf_id = doc['pf_ids'][0] if pf_id is not None: name += '-PF-{}'.format(pf_id) # if swaps, prepend new composition if subcmd == 'swaps': new_formula = get_formula_from_stoich(get_stoich( doc['atom_types'])) name = '{}-swap-{}'.format(new_formula, name) path = "{directory}/{prefix}{name}".format(directory=directory, prefix=prefix, name=name) if param: doc2param(doc, path, hash_dupe=hash_dupe) if cell: doc2cell(doc, path, hash_dupe=hash_dupe) if res: doc2res(doc, path, info=info, hash_dupe=hash_dupe) if json: doc2json(doc, path, hash_dupe=hash_dupe) if pdb: doc2pdb(doc, path, hash_dupe=hash_dupe) if xsf: doc2xsf(doc, path) hull = subcmd in ['hull', 'voltage'] if isinstance(cursor, pm.cursor.Cursor): cursor.rewind() md_path = "{directory}/{directory}.md".format(directory=directory) md_kwargs = {} md_kwargs.update(kwargs) md_kwargs.update({ 'markdown': True, 'latex': False, 'argstr': argstr, 'hull': hull }) md_string = display_results(cursor, **md_kwargs) with open(md_path, 'w') as f: f.write(md_string) if latex: if isinstance(cursor, pm.cursor.Cursor): cursor.rewind() tex_path = "{directory}/{directory}.tex".format(directory=directory) print('Writing LaTeX file', tex_path + '...') tex_kwargs = {} tex_kwargs.update(kwargs) tex_kwargs.update({ 'latex': True, 'markdown': False, 'argstr': argstr, 'hull': hull }) tex_string = display_results(cursor, **tex_kwargs) with open(tex_path, 'w') as f: f.write(tex_string) print('Done!')
def __repr__(self): return display_results(self.hull_cursor, args=self.args, hull=True, energy_key=self.energy_key, return_str=True)