Skip to content

Commit

Permalink
Merge pull request #21 from LaurentRDC/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
LaurentRDC committed Jul 27, 2017
2 parents e388869 + 9a83653 commit 9f07ca4
Show file tree
Hide file tree
Showing 19 changed files with 1,053 additions and 537 deletions.
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ environment:
TEST_CMD: "python -m unittest discover --verbose"

CONDA_DEPENDENCIES: "numpy scipy cython scikit-image"
PIP_DEPENDENCIES: "pywavelets spglib pycifrw"
PIP_DEPENDENCIES: "pywavelets spglib pycifrw ase"

matrix:

Expand Down
6 changes: 2 additions & 4 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
numpy>=1.11
coverage==4.3.1
codecov==2.0.5
numpy>=1.12
Sphinx>=1.5.5
cython>=0.25
matplotlib
ase
22 changes: 13 additions & 9 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,32 +36,36 @@ Affine Transforms
=================
.. automodule:: skued.affine

Structure
=========
Crystal structure
=================
Handling crystal structure information is crucial for many data analysis and modelling tasks.
See the :ref:`Structure tutorial <structure_tutorial>` for some examples on how to use the following
classes.


.. autoclass:: skued.Crystal
:members:

.. autoattribute:: skued.Crystal.lattice_vectors

.. autoattribute:: skued.Crystal.lattice_parameters

.. autoclass:: skued.Atom
:members:

.. autoclass:: skued.Lattice
:members:

.. autoclass:: skued.Crystal
:members:
.. autofunction:: skued.structure.symmetry_expansion

Simulation
==========
.. autofunction:: skued.simulation.powdersim

Baseline-determination
======================
.. autofunction:: skued.baseline.baseline_dt

.. autofunction:: skued.baseline.baseline_dwt
.. autofunction:: skued.baseline_dt

.. autofunction:: skued.baseline.dtcwt
.. autofunction:: skued.baseline_dwt

Image Analysis
==============
Expand Down
8 changes: 4 additions & 4 deletions docs/source/tutorials/baseline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Here is a usage example for the data presented above::

import numpy as np
from skued import gaussian
from skued.baseline import baseline_dwt
from skued import baseline_dwt

s, intensity = np.load('powder.npy')

Expand All @@ -103,7 +103,7 @@ Here is a usage example for the data presented above::
import matplotlib.pyplot as plt
import numpy as np
from skued import gaussian, spectrum_colors
from skued.baseline import baseline_dwt
from skued import baseline_dwt

s, intensity = np.load('powder.npy')

Expand Down Expand Up @@ -153,7 +153,7 @@ Here is a usage example for the data presented above::

import numpy as np
from skued import gaussian
from skued.baseline import baseline_dt
from skued import baseline_dt

s, intensity = np.load('powder.npy')

Expand All @@ -169,7 +169,7 @@ Here is a usage example for the data presented above::
import matplotlib.pyplot as plt
import numpy as np
from skued import gaussian, spectrum_colors
from skued.baseline import baseline_dt
from skued import baseline_dt

s, intensity = np.load('powder.npy')

Expand Down
164 changes: 101 additions & 63 deletions docs/source/tutorials/structure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,63 +11,9 @@ Modeling atomic structures
Contents
========

* :ref:`Atom`
* :ref:`Crystal`
* :ref:`Atom`

.. _atom:

The :class:`Atom` Class
=======================
The basis of structure manipulations is to manipulate atoms. :class:`Atom` objects are in the
category of `Transformable` objects, meaning that their coordinates can be transformed
according to any affine transform.

To create an atom, simply provide its element and coordinates::
from skued import Atom

copper = Atom(element = 'Cu', coords = [0,0,0])

Since we are most concerned with atoms in crystals, the coordinates here are assumed to be fractional.
The real-space position with respect to a :class:`Crystal` or :class:`Lattice` can be accessed using the
:meth:`xyz` method::

from skued.structure import graphite
carbon = list(graphite)[-1]
fractional = carbon.coords
real = carbon.xyz(lattice = graphite)

One important feature of the :class:`Atom` class is the possibility to compute the electrostatic
potential across meshes::

import numpy as np
import matplotlib.pyplot as plt

xx, yy = np.meshgrid(np.linspace(-0.3, 0.3, num = 100),
np.linspace(-0.3, 0.3, num = 100))
dist = np.sqrt(xx**2 + yy**2) # distance from the atom in Angstroms

es_potential = copper.potential(dist)
plt.imshow(es_potential)

After plot formatting:

.. plot::

import numpy as np
import matplotlib.pyplot as plt
from skued.structure import Atom
copper = Atom(element = 'Cu', coords = [0,0,0])
xx, yy = np.meshgrid(np.linspace(-0.3, 0.3, num = 100),
np.linspace(-0.3, 0.3, num = 100))
dist = np.sqrt(xx**2 + yy**2) # distance from the atom in Angstroms
es_potential = copper.potential(dist)
plt.title('Atomic potential of Cu (log-scale)')
plt.imshow(np.log(1 + es_potential), extent = [xx.min(), xx.max(), yy.min(), yy.max()])
plt.ylabel('x-direction ($\AA$)')
plt.xlabel('y-direction ($\AA$)')
plt.show()

.. _crystal:

Expand Down Expand Up @@ -112,9 +58,7 @@ To do this, you need:
1. iterable of :class:`Atom` objects, with coordinates. These atoms can either be the full unit cell
or the asymmetric unit cell;
2. three lattice vectors;
3. Symmetry operators (optional). These symmetry operators will be applied to the atoms to generate
the full unit cell. Hence, if your iterable of atoms contains the entire unit cell, symmetry operators do
not need to be provided. The symmetry operators must be expressed in the reduced (or fractional) basis.
3. (optional) symmetry operators that generate the full unit cell from the asymmetric unit cell.

As an example, let's create the simplest crystal structure known:
`alpha-Polonium (simple cubic) <https://en.wikipedia.org/wiki/Polonium#Solid_state_form>`_::
Expand All @@ -123,11 +67,12 @@ As an example, let's create the simplest crystal structure known:
import numpy as np

lattice_vectors = 3.35 * np.eye(3)
atoms = [Atom('Po', coords = [0,0,0])]
unitcell = [Atom('Po', coords = [0,0,0])]

polonium = Crystal(atoms = atoms, lattice_vectors = lattice_vectors)
polonium = Crystal(unitcell, lattice_vectors)

That's it!
In the case where atoms are given as an asymmetric unit cell and a set of symmetry operators, you can use the
:func:`symmetry_expansion` function to generate a set of *unique* atoms (even if some symmetry operators might be redundant).

Crystal attributes
------------------
Expand All @@ -137,9 +82,26 @@ The :class:`Crystal` object provides some interfaces for easy structure manipula

for atm in graphite: #Loops over atoms in the unit cell
print(atm.element, atm.coords)
The :func:`len` of a :class:`Crystal` is the unit cell size (in number of atoms)::

from skued import Crystal

c = Crystal.from_pdb('1gzx') # hemoglobin
len(c) # 17536

:class:`Crystal` instances can be equated to each other::

Note that iterating over the :attr:`crystal.atoms` attribute may or may not be equivalent to
:data:`iter(crystal)`, due to the way symmetry operators are defined.
gold = Crystal.from_database('Au')
silver = Crystal.from_database('Ag')

assert gold == silver # false

If a :class:`Crystal` was generated from a file, the path to its file can be retrieved
from the :attr:`source` attribute::

c = Crystal.from_pdb('1gzx')
print(c.source)

Lattice vectors and reciprocal space
-------------------------------------
Expand Down Expand Up @@ -222,4 +184,80 @@ Static structure factor calculation is also possible, both for a single reflecti
SF = graphite.structure_factor_miller(h, k, l)
SF.shape == h.shape # True

Compatibility with ASE
----------------------
The `Atomic Simulation Environment <https://wiki.fysik.dtu.dk/ase/index.html>`_ is a powerful tool. You can harness its power and convert
between :class:`ase.Atoms` and :class:`skued.Crystal` at will.

To create an :class:`ase.Atoms` object from a :class:`Crystal`, use the :meth:`Crystal.ase_atoms` method::

from ase.calculators.abinit import Abinit
from skued import Crystal
gold = Crystal.from_database('Au')
ase_gold = gold.ase_atoms(calculator = Abinit(...))

All keywords of the :class:`ase.Atoms` constructor are supported. To get back to a :class:`Crystal` instance::

gold2 = Crystal.from_ase(ase_gold)

.. _atom:

The :class:`Atom` Class
=======================
The basis of structure manipulations is to manipulate atoms. :class:`Atom` objects are in the
category of `Transformable` objects, meaning that their coordinates can be transformed
according to any affine transform.

To create an atom, simply provide its element and coordinates::
from skued import Atom

copper = Atom(element = 'Cu', coords = [0,0,0])

Optional information can be give, such as magnetic moment and mean-squared displacement. For users of :mod:`ase`,
another possibility is to instantiate an :class:`Atom` from an :class:`ase.Atom` using the :meth:`Atom.from_ase`
constructor.

Since we are most concerned with atoms in crystals, the coordinates here are assumed to be fractional.
The real-space position with respect to a :class:`Crystal` or :class:`Lattice` can be accessed using the
:meth:`xyz` method::

from skued.structure import graphite
carbon = list(graphite)[-1]
fractional = carbon.coords
real = carbon.xyz(lattice = graphite)

One important feature of the :class:`Atom` class is the possibility to compute the electrostatic
potential across meshes::

import numpy as np
import matplotlib.pyplot as plt

xx, yy = np.meshgrid(np.linspace(-0.3, 0.3, num = 100),
np.linspace(-0.3, 0.3, num = 100))
dist = np.sqrt(xx**2 + yy**2) # distance from the atom in Angstroms

es_potential = copper.potential(dist)
plt.imshow(es_potential)

After plot formatting:

.. plot::

import numpy as np
import matplotlib.pyplot as plt
from skued.structure import Atom
copper = Atom(element = 'Cu', coords = [0,0,0])
xx, yy = np.meshgrid(np.linspace(-0.3, 0.3, num = 100),
np.linspace(-0.3, 0.3, num = 100))
dist = np.sqrt(xx**2 + yy**2) # distance from the atom in Angstroms
es_potential = copper.potential(dist)
plt.title('Atomic potential of Cu (log-scale)')
plt.imshow(np.log(1 + es_potential), extent = [xx.min(), xx.max(), yy.min(), yy.max()])
plt.ylabel('x-direction ($\AA$)')
plt.xlabel('y-direction ($\AA$)')
plt.show()

:ref:`Return to Top <structure_tutorial>`
1 change: 1 addition & 0 deletions skued/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
rotation_matrix, transform, translation_matrix,
translation_rotation_matrix)
from .array_utils import mirror, repeated_array
from .baseline import baseline_dt, baseline_dwt
from .iter_utils import chunked, last, linspace, multilinspace
from .parallel import pmap, preduce
from .plot_utils import spectrum_colors, rgb_sweep
Expand Down
5 changes: 3 additions & 2 deletions skued/structure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ class ParseError(IOError):
pass

from .atom import Atom, real_coords, frac_coords
from .atom_data import ELEM_TO_MAGMOM, ELEM_TO_MASS, ELEM_TO_NAME, ELEM_TO_NUM, NUM_TO_ELEM
from .lattice import Lattice, lattice_vectors_from_parameters
from .pdb_parser import PDBParser
from .spg_data import Hall2Number
from .cif_parser import CIFParser
from .crystal import Crystal
from .crystal import Crystal, symmetry_expansion

###########################
# Graphite built-in Crystal
Expand All @@ -35,4 +36,4 @@ class ParseError(IOError):
for coordinates in (r1,r2,r3,r4):
unitcell.append(Atom(element = 'C', coords = frac_coords(coordinates, lattice_vectors)))

graphite = Crystal(atoms = unitcell, lattice_vectors = lattice_vectors, symmetry_operators = [np.eye(3)])
graphite = Crystal(unitcell, lattice_vectors = lattice_vectors)

0 comments on commit 9f07ca4

Please sign in to comment.