def test_as_dict_and_from_dict(self): sm = StructureMatcher(ltol=0.1, stol=0.2, angle_tol=2, primitive_cell=False, scale=False, comparator=FrameworkComparator()) d = sm.as_dict() sm2 = StructureMatcher.from_dict(d) self.assertEqual(sm2.as_dict(), d)
def compare_structures(options): """Inspired to a similar function in pmg_structure.""" paths = options.paths if len(paths) < 2: print("You need more than one structure to compare!") return 1 try: structures = [abilab.Structure.from_file(p) for p in paths] except Exception as ex: print("Error reading structures from files. Are they in the right format?") print(str(ex)) return 1 from pymatgen.analysis.structure_matcher import StructureMatcher, ElementComparator compareby = "species" if options.anonymous else "element" m = StructureMatcher() if compareby == "species" else StructureMatcher(comparator=ElementComparator()) print("Grouping %s structures by `%s` with `anonymous: %s`" % (len(structures), compareby, options.anonymous)) for i, grp in enumerate(m.group_structures(structures, anonymous=options.anonymous)): print("Group {}: ".format(i)) for s in grp: spg_symbol, international_number = s.get_space_group_info() print("\t- {} ({}), vol: {:.2f} A^3, {} ({})".format( paths[structures.index(s)], s.formula, s.volume, spg_symbol, international_number)) print() if options.verbose: pprint(m.as_dict())
def group_entries_with_structure_matcher( g, struct_matcher: StructureMatcher, working_ion: str = None, ) -> Iterable[List[Union[ComputedStructureEntry]]]: """ Group the entries together based on similarity of the primitive cells Args: g: a list of entries struct_matcher: the StructureMatcher object used to aggregate structures working_ion: the name of the working ion, if none use the first ignored species from the structure_matcher Returns: subgroups: subgroups that are grouped together based on structure similarity """ if working_ion is None: wion = struct_matcher.as_dict()["ignored_species"][0] # Sort the entries by symmetry and by working ion fraction def get_num_sym_ops(ent): sga = SpacegroupAnalyzer(ent.structure) return len(sga.get_space_group_operations()) g.sort(key=get_num_sym_ops, reverse=True) g.sort(key=lambda x: x.composition.get_atomic_fraction(wion)) labs = generic_groupby( g, comp=lambda x, y: struct_matcher.fit( x.structure, y.structure, symmetric=True), ) for ilab in set(labs): sub_g = [g[itr] for itr, jlab in enumerate(labs) if jlab == ilab] yield [el for el in sub_g]
def get_ion_insertion_wf( structure: Structure, working_ion: str, structure_matcher: StructureMatcher = None, db_file: str = DB_FILE, vasptodb_kwargs: dict = None, volumetric_data_type: str = "CHGCAR", vasp_powerups: List[dict] = None, attempt_insertions: int = 4, max_inserted_atoms: int = None, allow_fizzled_parents: bool = True, optimizefw_kwargs: dict = None, staticfw_kwargs: dict = None, ): """ Take the output static worflow and iteratively insert working ions based on charge density analysis. The workflow performs the following tasks. (StaticFW) <- Recieved dat inserted task_id from this workflow (AnalyzeChgcar) <- Obtain the set of possible unique insertions using the stored charge density (GetInsertionCalcs) <- This task contains the dynamic workflow creation that will keep inserting working ions Args: structure: The host structure to begin inserting on working_ion: The working ion to be inserted at each step structure_matcher: StructureMatcher object used to define topotactic insertion db_file: The db_file that defines the VASP output database vasptodb_kwargs: vasptodb_kwargs for the static workflow volumetric_data_type: the type of volumetric data used to determine the insertion sites vasp_powerups: additional powerups given to all the dynamically created workflows optimizefw_kwargs: additional kwargs for all the OptimizeFWs max_inserted_atoms: the limit on the total number of ions to insert attempt_insertions: number of insertions sites to run at each insertion step staticfw_kwargs: additional kwargs for all the StaticFWs """ if not structure.is_ordered: raise ValueError( "Please obtain an ordered approximation of the input structure.") if optimizefw_kwargs is None: optimizefw_kwargs = {} if staticfw_kwargs is None: staticfw_kwargs = {} # Configured the optimization and static FWs for the base material vasptodb_kwargs = vasptodb_kwargs if vasptodb_kwargs is not None else {} vasptodb_kwargs_vol_data = { "CHGCAR": ["CHGCAR"], "AECCAR": ["AECCAR0", "AECCAR2"] } vasptodb_kwargs.update({ "store_volumetric_data": vasptodb_kwargs_vol_data[volumetric_data_type], "task_fields_to_push": { "base_task_id": "task_id" }, }) opt_wf = OptimizeFW(structure=structure, db_file=db_file, **optimizefw_kwargs) pass_task = pass_vasp_result( filename="vasprun.xml.relax2.gz", pass_dict=">>output.ionic_steps.-1.structure", mod_spec_key="prev_calc_structure", ) opt_wf.tasks.append(pass_task) static_wf = StaticFW(structure=structure, vasptodb_kwargs=vasptodb_kwargs, db_file=db_file, parents=[opt_wf], spec_structure_key="prev_calc_structure", **staticfw_kwargs) wf_name = "{}-{}".format( structure.composition.reduced_formula if structure else "unknown", "insertion", ) # Configure the analysis FW analysis_wf = Firework( [AnalyzeChgcar(), GetInsertionCalcs()], parents=[static_wf], name="Charge Density Analysis-0", ) analysis_wf.spec["working_ion"] = working_ion # Crate the initial workflow wf = Workflow([opt_wf, static_wf, analysis_wf], name=wf_name) # Apply the vasp powerup if present if vasp_powerups is not None: wf = powerup_by_kwargs(wf, vasp_powerups) for fw in wf.fws: fw.spec["vasp_powerups"] = vasp_powerups if structure_matcher is not None: sm_dict = structure_matcher.as_dict() for fw in wf.fws: fw.spec["structure_matcher"] = sm_dict # write the persistent specs to all the fws # Note this is probably redundant but is easier for fw in wf.fws: fw.spec["db_file"] = db_file fw.spec["attempt_insertions"] = attempt_insertions fw.spec["vasptodb_kwargs"] = vasptodb_kwargs fw.spec["staticfw_kwargs"] = staticfw_kwargs fw.spec["optimizefw_kwargs"] = optimizefw_kwargs fw.spec["allow_fizzled_parents"] = allow_fizzled_parents fw.spec["volumetric_data_type"] = volumetric_data_type fw.spec["max_inserted_atoms"] = max_inserted_atoms return wf