Skip to content
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

Upgrade the aimsgb structure factory #1123

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 89 additions & 57 deletions pyiron_atomistics/atomistics/structure/factories/aimsgb.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
# Copyright (c) Max-Planck-Institut für Eisenforschung GmbH - Computational Materials Design (CM) Department
# Distributed under the terms of "New BSD License", see the LICENSE file.

from structuretoolkit.build import grainboundary, get_grainboundary_info
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the structure toolkit interface.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The interface is pretty similar and this is what is used by pyiron_atomistics in the background https://github.com/pyiron/structuretoolkit/blob/main/structuretoolkit/build/aimsgb.py

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened a jupyter notebook from the jupyterhub session runnig at cmti and tried to import from structuretoolkit.common.pymatgen import ase_to_pymatgen, pymatgen_to_ase and got this error ModuleNotFoundError: No module named 'structuretoolkit.common'

from structuretoolkit.build import grainboundary, get_grainboundary_info also gave me the error ImportError: cannot import name 'grainboundary' from 'structuretoolkit.build' (/u/system/SLES12/soft/pyiron/dev/anaconda3/lib/python3.10/site-packages/structuretoolkit/build/__init__.py)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I got this. I have to modify the import statement from from structuretoolkit.build import grainboundary, get_grainboundary_info to from structuretoolkit.build.aimsgb import grainboundary_info, grainboundary_build

But, still can't figure out what is the new alternative of structuretoolkit.common.pymatgen import ase_to_pymatgen, pymatgen_to_ase

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I am confused, because it is available in the official package https://github.com/pyiron/structuretoolkit/blob/main/structuretoolkit/common/pymatgen.py . Which version of structure toolkit is installed on cmti? You can check the version using conda list structuretoolkit

Copy link
Contributor Author

@usaikia usaikia Jul 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output of conda list structuretoolkit is as follows:

packages in environment at /u/system/SLES12/soft/pyiron/dev/anaconda3:

Name Version Build Channel
structuretoolkit 0.0.1 pyhd8ed1ab_1 conda-forge

Note: you may need to restart the kernel to use updated packages.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, this is a rather old version - the current version is 0.0.7.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jan-janssen Thanks! I noticed that the current incompatibility issue is coming from this line AseAtomsAdaptor.get_structure(initial_struct, cls=None). The option cls=None is no longer accepted. We can change it to AseAtomsAdaptor.get_structure(initial_struct) which will take the default value which is pymatgen.Structure

from pyiron_atomistics.atomistics.structure.atoms import ase_to_pyiron
from pyiron.atomistics.structure.atoms import pyiron_to_pymatgen, pymatgen_to_pyiron
from pymatgen.io.ase import AseAtomsAdaptor
from aimsgb import GBInformation, GrainBoundary, Grain


__author__ = "Ujjal Saikia"
__copyright__ = (
Expand All @@ -19,75 +21,105 @@

class AimsgbFactory:
@staticmethod
def info(axis, max_sigma):
def info(axis,
max_sigma=15,
specific=False,
table_view=False
):
"""
Provides a list of possible GB structures for a given rotational axis and upto the given maximum sigma value.

A dictionary with information including possible sigma, CSL matrix, GB plane,
rotation angle and rotation matrix.
Args:
axis : Rotational axis for the GB you want to construct (for example, axis=[1,0,0])
max_sigma (int) : The maximum value of sigma upto which you want to consider for your
GB (for example, sigma=5)
axis ([u, v, w]): Rotation axis.
max_sigma (int): The largest sigma value. Dafult value is 15.
specific (bool): Whether collecting information for a specific sigma.
Dafult value is False.
table_view (bool): Display output in a tabuler format. Default value is True.

Returns:
A list of possible GB structures in the format:
A aimsgb.GBInformation object or display a table depending on
the value of table_view parameter.

{sigma value: {'theta': [theta value],
'plane': the GB planes")
'rot_matrix': array([the rotational matrix]),
'csl': [array([the csl matrix])]}}

To construct the grain boundary select a GB plane and sigma value from the list and pass it to the
GBBuilder.gb_build() function along with the rotational axis and initial bulk structure.
To construct the grain boundary select a GB plane and sigma value from
the table and pass it to the GBBuilder.build() function along with
the rotational axis and initial bulk structure.
"""
return get_grainboundary_info(axis=axis, max_sigma=max_sigma)
if table_view:
return print(GBInformation(axis, max_sigma, specific).__str__())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return print(...) looks super weird. Is it really not enough to just return __str__()? or print() without the return?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liamhuber Thanks a lot for the feedback. I will implement the changes you suggested.

else:
return GBInformation(axis, max_sigma, specific)

@staticmethod
def build(
axis,
sigma,
plane,
initial_struct,
to_primitive=False,
delete_layer="0b0t0b0t",
add_if_dist=0.0,
uc_a=1,
uc_b=1,
):
def build(axis,
sigma,
plane,
initial_struct,
uc_a=1,
uc_b=1,
vacuum=0.0,
gap=0.0,
delete_layer='0b0t0b0t',
tol=0.25,
to_primitive=False
):
"""
Generate a grain boundary structure based on the aimsgb.GrainBoundary module.
A grain boundary (GB) object. The initial structure can be cubic or non-cubic
crystal. If non-cubic, the crystal will be transferred to conventional cell.
The generated GB could be either tilted or twisted based on the given GB
plane. If the GB plane is parallel to the rotation axis, the generated GB
will be a twisted one. Otherwise, tilted. Build grain boundary based on
rotation axis, sigma, GB plane, grain size, initial_struct and vacuum thickness.
Build an interface structure by stacking two grains along a given direction.
The grain_b a- and b-vectors will be forced to be the grain_a's a- and b-vectors.

Args:
axis : Rotational axis for the GB you want to construct (for example, axis=[1,0,0])
sigma (int) : The sigma value of the GB you want to construct (for example, sigma=5)
plane: The grain boundary plane of the GB you want to construct (for example, plane=[2,1,0])
initial_struct : Initial bulk structure from which you want to construct the GB (a pyiron
structure object).
delete_layer : To delete layers of the GB. For example, delete_layer='1b0t1b0t'. The first
4 characters is for first grain and the other 4 is for second grain. b means
bottom layer and t means top layer. Integer represents the number of layers
to be deleted. The first t and second b from the left hand side represents
the layers at the GB interface. Default value is delete_layer='0b0t0b0t', which
means no deletion of layers.
add_if_dist : If you want to add extra interface distance, you can specify add_if_dist.
Default value is add_if_dist=0.0
to_primitive : To generate primitive or non-primitive GB structure. Default value is
to_primitive=False
axis ([u, v, w]): Rotation axis.
sigma (int): The area ratio between the unit cell of CSL and the
given crystal lattice.
plane ([h, k, l]): Miller index of GB plane. If the GB plane is parallel
to the rotation axis, the generated GB will be a twist GB. If they
are perpendicular, the generated GB will be a tilt GB.
initial_struct (Grain): Initial input structure. Must be an object of
pyiron_atomistics.atomistics.structure.atoms.Atoms
uc_a (int): Number of unit cell of grain A. Default to 1.
uc_b (int): Number of unit cell of grain B. Default to 1.
vacuum (float): Vacuum space between the surface of the grains in Angstroms. Default to 0.0
gap (float): Gap between the GB interface of the grains in Angstroms. Default to 0.0
delete_layer (str): Delete top and bottom layers of the first and the second grains.
8 characters in total. The first 4 characters is for the first grain and
the other 4 is for the second grain. "b" means bottom layer and "t" means
top layer. Integer represents the number of layers to be deleted.
Default to "0b0t0b0t", which means no deletion of layers. The
direction of top and bottom layers is based on the given direction.
tol (float): Tolerance factor in Angstrom to determnine if sites are
in the same layer. Default to 0.25.
to_primitive (bool): Whether to get primitive structure of GB. Default to False.


Returns:
:class:`.Atoms`: final grain boundary structure
"""
return ase_to_pyiron(
grainboundary(
axis=axis,
sigma=sigma,
plane=plane,
initial_struct=initial_struct,
to_primitive=to_primitive,
delete_layer=delete_layer,
add_if_dist=add_if_dist,
uc_a=uc_a,
uc_b=uc_b,
)
)
basis_pmg = AseAtomsAdaptor.get_structure(initial_struct)

basis_pmg = Grain(lattice=basis_pmg.lattice,
species=basis_pmg.species,
coords=basis_pmg.frac_coords)

gb = GrainBoundary(axis=axis,
sigma=sigma,
plane=plane,
initial_struct=basis_pmg,
uc_a=uc_a,
uc_b=uc_b)

structure = Grain.stack_grains(grain_a=gb.grain_a,
grain_b=gb.grain_b,
vacuum=vacuum,
gap=gap,
direction=gb.direction,
delete_layer=delete_layer,
tol=tol,
to_primitive=to_primitive)

return pymatgen_to_pyiron(structure)