-
Notifications
You must be signed in to change notification settings - Fork 144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adding ASE compatibility to Crystal class #534
Closed
Closed
Changes from 14 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
8eb4c31
adding new io from ASE features
alex-rakowski 677fa8c
updating docstring
alex-rakowski b3b1829
black formatting
alex-rakowski 4c0fd9b
removing generic adding prismatic
alex-rakowski f9a2ad8
docstring typo
alex-rakowski 3f31ba8
black format
alex-rakowski 00bee58
adding ase requirement
alex-rakowski 358edb6
adding occupancy to crystal object and loader
alex-rakowski dda28cb
adding file IDs to downloads
alex-rakowski f6529dc
changing to lowercase...
alex-rakowski 8f9574b
Changing CIF from pymatgen to ase and lowercase
alex-rakowski 763a00b
adding tests
alex-rakowski ef64b1f
run black
alex-rakowski 87f9477
run black
alex-rakowski 3b6f117
updates
bsavitzky 3465e16
formats with black
bsavitzky File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,12 +14,19 @@ | |
from py4DSTEM.process.diffraction.crystal_viz import plot_ring_pattern | ||
from py4DSTEM.process.diffraction.utils import Orientation, calc_1D_profile | ||
|
||
from importlib.util import find_spec | ||
|
||
try: | ||
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer | ||
from pymatgen.core.structure import Structure | ||
except ImportError: | ||
pass | ||
|
||
try: | ||
from ase.io import read | ||
except ImportError: | ||
pass | ||
|
||
|
||
class Crystal: | ||
""" | ||
|
@@ -147,6 +154,9 @@ def __init__( | |
# Calculate lattice parameters | ||
self.calculate_lattice() | ||
|
||
# Set occupancy attribute to None | ||
self.occupancy = None | ||
|
||
def calculate_lattice(self): | ||
if not hasattr(self, "lat_real"): | ||
# calculate unit cell lattice vectors | ||
|
@@ -265,27 +275,27 @@ def get_strained_crystal( | |
else: | ||
return crystal_strained | ||
|
||
def from_CIF(CIF, conventional_standard_structure=True): | ||
""" | ||
Create a Crystal object from a CIF file, using pymatgen to import the CIF | ||
# def from_CIF(CIF, conventional_standard_structure=True): | ||
# """ | ||
# Create a Crystal object from a CIF file, using pymatgen to import the CIF | ||
|
||
Note that pymatgen typically prefers to return primitive unit cells, | ||
which can be overridden by setting conventional_standard_structure=True. | ||
# Note that pymatgen typically prefers to return primitive unit cells, | ||
# which can be overridden by setting conventional_standard_structure=True. | ||
|
||
Args: | ||
CIF: (str or Path) path to the CIF File | ||
conventional_standard_structure: (bool) if True, conventional standard unit cell will be returned | ||
instead of the primitive unit cell pymatgen typically returns | ||
""" | ||
from pymatgen.io.cif import CifParser | ||
# Args: | ||
# CIF: (str or Path) path to the CIF File | ||
# conventional_standard_structure: (bool) if True, conventional standard unit cell will be returned | ||
# instead of the primitive unit cell pymatgen typically returns | ||
# """ | ||
# from pymatgen.io.cif import CifParser | ||
|
||
parser = CifParser(CIF) | ||
# parser = CifParser(CIF) | ||
|
||
structure = parser.get_structures()[0] | ||
# structure = parser.get_structures()[0] | ||
|
||
return Crystal.from_pymatgen_structure( | ||
structure, conventional_standard_structure=conventional_standard_structure | ||
) | ||
# return Crystal.from_pymatgen_structure( | ||
# structure, conventional_standard_structure=conventional_standard_structure | ||
# ) | ||
|
||
def from_pymatgen_structure( | ||
structure=None, | ||
|
@@ -400,6 +410,149 @@ def from_pymatgen_structure( | |
|
||
return Crystal(positions, numbers, cell) | ||
|
||
def from_ase( | ||
atoms, | ||
): | ||
""" | ||
Create a py4DSTEM Crystal object from an ASE atoms object | ||
|
||
Args: | ||
atoms (ase.Atoms): an ASE atoms object | ||
|
||
""" | ||
|
||
xtal = Crystal( | ||
positions=atoms.get_scaled_positions(), # fractional coords | ||
numbers=atoms.numbers, | ||
cell=atoms.cell.array, | ||
) | ||
|
||
# get occupancies | ||
# ASE seems to have different ways of storing occupancies | ||
# if ASE object created from prismatic file | ||
if "occupancies" in atoms.arrays.keys(): | ||
# np.array with length number of atoms | ||
xtal.occupancy = atoms.arrays["occupancies"] | ||
# if created from cif file | ||
elif "occupancy" in atoms.info.keys(): | ||
# python dict with occupancy per site | ||
xtal.occupancy = atoms.info["occupancy"] | ||
# TODO add in elif statement if other ways appear | ||
else: | ||
print(Warning("Could not find occupancies of crystal")) | ||
return xtal | ||
|
||
def from_prismatic(filepath): | ||
""" | ||
Create a py4DSTEM Crystal object from an prismatic style xyz co-ordinate file | ||
|
||
Args: | ||
filepath (str|Pathlib.Path): path to the prismatic format xyz file | ||
|
||
""" | ||
|
||
# check if ase is installed | ||
if find_spec("ase") is None: | ||
raise ImportWarning("Could not import ASE, please install, and try again") | ||
else: | ||
from ase.io import read | ||
|
||
atoms = read(filepath, format="prismatic") | ||
|
||
# create the crystal object | ||
xtal = Crystal( | ||
positions=atoms.get_scaled_positions(), # fractional coords | ||
numbers=atoms.numbers, | ||
cell=atoms.cell.array, | ||
) | ||
# add occupancies | ||
# It should be this one but keeping other method in case | ||
if "occupancies" in atoms.arrays.keys(): | ||
# np.array with length number of atoms | ||
xtal.occupancy = atoms.arrays["occupancies"] | ||
# if created from cif file | ||
elif "occupancy" in atoms.info.keys(): | ||
# python dict with occupancy per site | ||
xtal.occupancy = atoms.info["occupancy"] | ||
# TODO add in elif statement if other ways appear | ||
else: | ||
print(Warning("Could not find occupancies of crystal")) | ||
return xtal | ||
|
||
def from_cif( | ||
filepath, | ||
): | ||
""" | ||
Create a py4DSTEM Crystal object from a cif file using ase.io.read function | ||
|
||
Args: | ||
filepath (str|Pathlib.Path): path to the file | ||
""" | ||
|
||
# check if ase is installed | ||
if find_spec("ase") is None: | ||
raise ImportWarning( | ||
"Could not import ASE, please install, restart and try again" | ||
) | ||
else: | ||
from ase.io import read | ||
Comment on lines
+493
to
+498
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is all this extra logic necessary? If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to delete as its now a dependency |
||
# try loading the file using ase read and get required properties | ||
atoms = read(filepath, format="cif") | ||
xtal = Crystal( | ||
positions=atoms.get_scaled_positions(), # fractional coords | ||
numbers=atoms.numbers, | ||
cell=atoms.cell.array, | ||
) | ||
|
||
# add occupancies | ||
# It should be this one but keeping other method in case | ||
if "occupancies" in atoms.arrays.keys(): | ||
# np.array with length number of atoms | ||
xtal.occupancy = atoms.arrays["occupancies"] | ||
# if created from cif file | ||
elif "occupancy" in atoms.info.keys(): | ||
# python dict with occupancy per site | ||
xtal.occupancy = atoms.info["occupancy"] | ||
# TODO add in elif statement if other ways appear | ||
# TODO when nested python dict, it needs to be unpacked. into numpy array like from_prismatic | ||
else: | ||
print(Warning("Could not find occupancies of crystal")) | ||
return xtal | ||
|
||
# def from_generic_file(filepath, **kwargs): | ||
# """ | ||
# Create a py4DSTEM Crystal from a wide range of generic file types using | ||
# `ase.io.read`, kwargs are passed to `ase.io.read` function. For more details | ||
# and potentially compatible filetypes please see https://wiki.fysik.dtu.dk/ase/ase/io/io.html. | ||
# Note this has not been tested extensively. The loaded file must have these three properties: | ||
# .get_scaled_positions() | ||
# .numbers | ||
# .cell.array, | ||
|
||
# Args: | ||
# filepath (str|Pathlib.Path): path to the file | ||
# kwargs: key word arguments to be passed to `ase.io.read` | ||
|
||
# """ | ||
|
||
# # check if ase is installed | ||
# if find_spec("ase") is None: | ||
# raise ImportWarning( | ||
# "Could not import ASE, please install, restart and try again" | ||
# ) | ||
# else: | ||
# from ase.io import read | ||
# # try loading the file using ase read and get required properties | ||
# try: | ||
# atoms = read(filepath, **kwargs) | ||
# return Crystal( | ||
# positions=atoms.get_scaled_positions(), # fractional coords | ||
# numbers=atoms.numbers, | ||
# cell=atoms.cell.array, | ||
# ) | ||
# except Exception as e: | ||
# raise e | ||
|
||
def from_unitcell_parameters( | ||
latt_params, | ||
elements, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For an optional dependency that is not needed at the top level of the file, I think a better pattern is to move the import within the function that requires it. That way any import errors get raised at the appropriate moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to delete as its now a dependency