def symmetry(self, symprec=1e-3): """ Compute space group with spglib. """ from matador.utils.cell_utils import doc2spg import spglib as spg print('Refining symmetries...') if self.mode == 'display': print_warning('{}'.format('At symprec: ' + str(symprec))) print_warning("{:^36}{:^16}{:^16}".format('text_id', 'new sg', 'old sg')) for _, doc in enumerate(self.cursor): try: spg_cell = doc2spg(doc) sg = spg.get_spacegroup(spg_cell, symprec=symprec).split(' ')[0] if sg != doc['space_group']: self.changed_count += 1 self.diff_cursor.append(doc) if self.mode == 'display': print_notify("{:^36}{:^16}{:^16}" .format(doc['text_id'][0]+' '+doc['text_id'][1], sg, doc['space_group'])) doc['space_group'] = sg else: if self.mode == 'display': print("{:^36}{:^16}{:^16}" .format(doc['text_id'][0]+' '+doc['text_id'][1], sg, doc['space_group'])) except Exception: self.failed_count += 1 if self.args.get('debug'): print_exc() print_failure('Failed for' + ' '.join(doc['text_id']))
def spawn(self, join=False): """ Spawn processes to perform calculations. Keyword arguments: join (bool): whether or not to attach to ComputeTask process. Useful for testing. """ procs = [] error_queue = mp.Queue() for proc_id in range(self.nprocesses): procs.append( mp.Process(target=self.perform_new_calculations, args=(random.sample(self.file_lists['res'], len(self.file_lists['res'])), error_queue, proc_id))) for proc in procs: proc.start() if join: proc.join() errors = [] failed_seeds = [] # wait for each proc to write to error queue try: for _, proc in enumerate(procs): result = error_queue.get() if isinstance(result[1], Exception): errors.append(result) failed_seeds.append(result[2]) if errors: error_message = '' for error in errors: error_message += 'Process {} raised error(s): {}. '.format( error[0], error[1]) if len({type(error[1]) for error in errors}) == 1: raise errors[0][1] raise type(errors[0][1])(error_message) raise BundledErrors(error_message) # the only errors that reach here are fatal, e.g. WalltimeError, CriticalError, InputError, KeyboardInterrupt except RuntimeError as err: result = [proc.join(timeout=2) for proc in procs] result = [proc.terminate() for proc in procs if proc.is_alive()] print_failure('Fatal error(s) reported:') print_warning(err) raise err print('Nothing left to do.')
def wrapped_plot_function(*args, **kwargs): """ Wrap and return the plotting function. """ saving = False result = None # if we're going to be saving a figure, switch to Agg to avoid X-forwarding try: for arg in args: if arg.savefig: import matplotlib # don't warn as backend might have been set externally by e.g. Jupyter matplotlib.use('Agg', force=False) saving = True break except AttributeError: pass if not saving: if any(kwargs.get(ext) for ext in SAVE_EXTS): import matplotlib matplotlib.use('Agg', force=False) saving = True settings = load_custom_settings(kwargs.get('config_fname'), quiet=True, no_quickstart=True) try: style = settings.get('plotting', {}).get('default_style') if kwargs.get('style'): style = kwargs['style'] if style is not None and not isinstance(style, list): style = [style] if style is None: style = ['matador'] if 'matador' in style: for ind, styles in enumerate(style): if styles == 'matador': style[ind] = MATADOR_STYLE # now actually call the function set_style(style) result = function(*args, **kwargs) except Exception as exc: if 'TclError' not in type(exc).__name__: raise exc print_failure('Caught exception: {}'.format(type(exc).__name__)) print_warning('Error message was: {}'.format(exc)) print_warning('This is probably an X-forwarding error') print_failure('Skipping plot...') return result
def __init__(self, *args, **kwargs): """ Initialise the query with command line arguments and return results. """ # read args self.kwargs = kwargs self.args = vars(args[0]) self.args['no_quickstart'] = self.kwargs.get('no_quickstart') self.argstr = kwargs.get('argstr') file_exts = ['cell', 'res', 'pdb', 'markdown', 'latex', 'param', 'xsf'] self.export = any([self.args.get(ext) for ext in file_exts]) self.subcommand = self.args.pop("subcmd") if self.subcommand != 'import': self.settings = load_custom_settings( config_fname=self.args.get('config'), debug=self.args.get('debug'), no_quickstart=self.args.get('no_quickstart')) result = make_connection_to_collection( self.args.get('db'), check_collection=(self.subcommand != "stats"), mongo_settings=self.settings) self.client, self.db, self.collections = result if self.subcommand == 'stats': self.stats() try: if self.subcommand == 'import': from matador.db import Spatula self.importer = Spatula(self.args) if self.subcommand == 'query': self.query = DBQuery(self.client, self.collections, **self.args) self.cursor = self.query.cursor if self.subcommand == 'swaps': from matador.swaps import AtomicSwapper self.query = DBQuery(self.client, self.collections, **self.args) if self.args.get('hull_cutoff') is not None: self.hull = QueryConvexHull(query=self.query, **self.args) self.swapper = AtomicSwapper(self.hull.hull_cursor, **self.args) else: self.swapper = AtomicSwapper(self.query.cursor, **self.args) self.cursor = self.swapper.cursor if self.subcommand == 'refine': from matador.db import Refiner self.query = DBQuery(self.client, self.collections, **self.args) if self.args.get('hull_cutoff') is not None: self.hull = QueryConvexHull(self.query, **self.args) self.refiner = Refiner(self.hull.cursor, self.query.repo, **self.args) else: self.refiner = Refiner(self.query.cursor, self.query.repo, **self.args) self.cursor = self.refiner.cursor if self.subcommand == 'hull' or self.subcommand == 'voltage': self.hull = QueryConvexHull(**self.args, voltage=self.subcommand == 'voltage', client=self.client, collections=self.collections) self.cursor = self.hull.hull_cursor if self.subcommand == 'changes': from matador.db import DatabaseChanges if len(self.collections) != 1: raise SystemExit( 'Cannot view changes of more than one collection at once.' ) if self.args.get('undo'): action = 'undo' else: action = 'view' changeset = self.args.get('changeset') if changeset is None: changeset = 0 DatabaseChanges([key for key in self.collections][0], changeset_ind=changeset, action=action, mongo_settings=self.settings, override=kwargs.get('no_quickstart')) if self.subcommand == 'hulldiff': from matador.hull.hull_diff import diff_hulls if self.args.get('compare') is None: raise SystemExit( 'Please specify which hulls to query with --compare.') diff_hulls(self.client, self.collections, **self.args) if self.export and self.cursor: from matador.export import query2files if self.args.get('write_n') is not None: self.cursor = [ doc for doc in self.cursor if len(doc['stoichiometry']) == self.args.get('write_n') ] if not self.cursor: print_failure('No structures left to export.') query2files(self.cursor, **self.args, argstr=self.argstr, subcmd=self.subcommand, hash_dupe=True) if self.args.get('view'): from matador.utils.viz_utils import viz if self.args.get('top') is None: self.top = len(self.cursor) else: self.top = self.args.get('top') if len(self.cursor[:self.top]) > 10: from time import sleep print_warning( 'WARNING: opening {} files with ase-gui...'.format( len(self.cursor))) print_warning( 'Please kill script within 3 seconds if undesired...') sleep(3) if len(self.cursor[:self.top]) > 20: print_failure( 'You will literally be opening that many windows, ' + 'I\'ll give you another 5 seconds to reconsider...') sleep(5) print_notify('It\'s your funeral...') sleep(1) for doc in self.cursor[:self.top]: viz(doc) if self.subcommand != 'import': self.client.close() except (RuntimeError, SystemExit, KeyboardInterrupt) as oops: if isinstance(oops, RuntimeError): print_failure(oops) elif isinstance(oops, SystemExit): print_warning(oops) try: self.client.close() except AttributeError: pass raise oops
def make_recipe(self, structure, sg): """ Construct PDF with diffpy. """ # construct a PDFContribution object pdf = PDFContribution("Contribution") # read experimental data try: pdf.loadData(self.input_file) except: print_failure('Failed to parse ' + self.input_file + '. Exiting...') exit() print('Constructing PDF object for', structure.title) pdf.setCalculationRange(self.xmin, self.xmax, self.dx) pdf.addStructure("Contribution", structure) # create FitRecipe to calculate PDF with chosen fit variable fit = FitRecipe() fit.addContribution(pdf) # configure variables and add to recipe if sg != 'xxx' and sg is not None: print(sg) spacegroup_params = constrainAsSpaceGroup(pdf.Contribution.phase, sg) else: cart_lat = abc2cart([[ structure.lattice.a, structure.lattice.b, structure.lattice.c ], [ structure.lattice.alpha, structure.lattice.beta, structure.lattice.gamma ]]) positions_frac = structure.xyz atomic_numbers = [] for atom in structure.element: atomic_numbers.append(get_atomic_number(atom)) cell = (cart_lat, positions_frac, atomic_numbers) sg = int( spg.get_spacegroup(cell, symprec=1e-2).split(' ')[1].replace( '(', '').replace(')', '')) spacegroup_params = constrainAsSpaceGroup(pdf.Contribution.phase, sg) # print('Space group parameters:') # print(', '.join([param.name for param in spacegroup_params])) # iterate through spacegroup params and activate them for param in spacegroup_params.latpars: fit.addVar(param) for param in spacegroup_params.xyzpars: fit.addVar(param, fixed=True) # these next parameters are taken from Martin's PDFht.py, # though I have a feeling that was not their origin... # set initial ADP parameters for param in spacegroup_params.adppars: fit.addVar(param, value=0.03, fixed=True) # overall scale of PDF and delta2 parameter for correlated motion - from PDFht.py fit.addVar(pdf.scale, 1, fixed=True) fit.restrain(pdf.scale, lb=0, ub=0.1, scaled=True) fit.addVar(pdf.Contribution.delta2, 5, fixed=True) fit.restrain(pdf.Contribution.delta2, lb=1, ub=10, scaled=True) # fix Qdamp based on information about "our beamline": yep, no idea fit.addVar(pdf.qdamp, 0.03, fixed=True) fit.restrain(pdf.qdamp, lb=0, ub=0.1, scaled=True) return fit