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

Add Symmetry plot method #306

Merged
merged 28 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
42eb037
add symmetry scatter method
harripj Mar 21, 2022
8cbe2e7
rename scatter to plot and tidy up
harripj Mar 22, 2022
196c7bb
add symmetry name to figure title
harripj Mar 22, 2022
c5ac018
add point_groups notebook
harripj Mar 22, 2022
946627e
add Symmetry.plot test
harripj Mar 22, 2022
9fa4583
add notebook to index.rst
harripj Mar 22, 2022
66e249c
update docstring, add markers to args
harripj Mar 22, 2022
1f200f8
improve coverage
harripj Mar 22, 2022
d8193d6
Update CHANGELOG.rst
harripj Mar 22, 2022
32042cd
update notebook title and MD formatting
harripj Mar 22, 2022
6347843
add plot to reference.rst
harripj Mar 25, 2022
fbbd642
update testing, remove marker tests
harripj Mar 25, 2022
6339f90
update docstrings, change plot orientation default arg to None
harripj Mar 25, 2022
bc24ee9
Update point_groups.ipynb
harripj Mar 25, 2022
009e6f2
remove blank line
harripj Mar 25, 2022
41cfec9
fix typo
harripj Mar 25, 2022
beb1ea9
fix method reference in docstring
harripj Mar 25, 2022
a73f148
fit test symmetry.plot raises
harripj Mar 25, 2022
39bccd7
retrigger checks
harripj Mar 25, 2022
a59cdbb
update package locations
harripj Mar 25, 2022
d273f81
Merge branch 'add_Symmetry_scatter' of https://github.com/harripj/ori…
harripj Mar 25, 2022
2ac334f
add %matplotlib inline
harripj Mar 25, 2022
8ad76c4
Merge remote-tracking branch 'upstream/master' into add_Symmetry_plot
harripj Mar 29, 2022
bd85667
fix marker default, add comment about formatting
harripj Mar 29, 2022
7131934
update with #307, fix tests
harripj Mar 29, 2022
0fa530b
update notebook and add comments
harripj Mar 29, 2022
e4057ad
remove marker color test to improve coverage
harripj Mar 29, 2022
546ceb5
update v_reproject calculation
harripj Mar 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Unreleased

Added
-----
- Point group `Symmetry` elements can now be viewed under the stereographic projection
using `Symmetry.plot()`. The notebook point_groups.ipynb has been added to the
documentation.
- Add `reproject` argument to `Vector3d.scatter()` which reprojects vectors located on
the hidden hemisphere to the visible hemisphere.
- `Rotation` objects can now be checked for equality. Equality is determined by
Expand Down
8 changes: 4 additions & 4 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@
intersphinx_mapping = {
"dask": ("https://docs.dask.org/en/latest", None),
"diffpy.structure": ("https://www.diffpy.org/diffpy.structure", None),
"h5py": ("http://docs.h5py.org/en/stable/", None),
"matplotlib": ("https://matplotlib.org", None),
"numpy": ("https://docs.scipy.org/doc/numpy", None),
"h5py": ("https://docs.h5py.org/en/stable", None),
"matplotlib": ("https://matplotlib.org/stable", None),
"numpy": ("https://numpy.org/doc/stable", None),
"python": ("https://docs.python.org/3", None),
"scipy": ("https://docs.scipy.org/doc/scipy/reference", None),
"scipy": ("https://docs.scipy.org/doc/scipy", None),
}

# Add any paths that contain templates here, relative to this directory.
Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Work using orix
clustering_across_fundamental_region_boundaries.ipynb
clustering_orientations.ipynb
uniform_sampling_of_orientation_space.ipynb
point_groups.ipynb

.. toctree::
:hidden:
Expand Down
127 changes: 127 additions & 0 deletions doc/point_groups.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"nbsphinx": "hidden"
},
"source": [
"This notebook is part of the orix documentation https://orix.rtfd.io. Links to the documentation won’t work from the notebook."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualizing point groups\n",
"\n",
"Point group symmetry operations are shown here under the stereographic projection.\n",
"\n",
"Vectors located on the upper (`z >= 0`) hemisphere are displayed as points (`o`), whereas vectors on the lower hemisphere are reprojected onto the upper hemisphere and shown as crosses (`+`) by default. For more information about plot formatting and visualization, see [Vector3d.scatter()](reference.rst#orix.vector.Vector3d.scatter).\n",
"\n",
"More explanation of these figures is provided at http://xrayweb.chem.ou.edu/notes/symmetry.html#point."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For example, the `O (432)` point group:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"nbsphinx-thumbnail"
]
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"\n",
"from orix.quaternion import symmetry\n",
harripj marked this conversation as resolved.
Show resolved Hide resolved
"\n",
"symmetry.O.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The stereographic projection of all point groups is shown below:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from matplotlib import pyplot as plt\n",
"import numpy as np\n",
"\n",
"from orix import plot\n",
"from orix.quaternion import Rotation\n",
"from orix.vector import Vector3d\n",
"\n",
"schoenflies = ['C1', 'Ci', 'C2x', 'C2y', 'C2z', 'Csx', 'Csy', 'Csz', 'C2h', 'D2', 'C2v', 'D2h', 'C4', 'S4', 'C4h', 'D4', 'C4v', 'D2d', 'D4h', 'C3', 'S6', 'D3x', 'D3y', 'D3', 'C3v', 'D3d', 'C6', 'C3h', 'C6h', 'D6', 'C6v', 'D3h', 'D6h', 'T', 'Th', 'O', 'Td', 'Oh']\n",
"\n",
"assert len(symmetry._groups) == len(schoenflies)\n",
"\n",
"schoenflies = [s for s in schoenflies if not (s.endswith('x') or s.endswith('y'))]\n",
"\n",
"assert len(schoenflies) == 32\n",
"\n",
"orientation = Rotation.from_axes_angles((-1, 8, 1), np.deg2rad(65))\n",
"\n",
"fig, ax = plt.subplots(nrows=8, ncols=4, figsize=(10, 20), subplot_kw=dict(projection='stereographic'))\n",
"ax = ax.ravel()\n",
"\n",
"for i, s in enumerate(schoenflies):\n",
" sym = getattr(symmetry, s)\n",
"\n",
" ori_sym = sym.outer(orientation)\n",
" v = (ori_sym * Vector3d.zvector())\n",
" \n",
" # reflection in the projection plane (x-y) is performed internally in\n",
" # Symmetry.plot() or when using the `reproject=True` argument for\n",
" # Vector3d.scatter()\n",
" v_reproject = Vector3d(v.data.copy())\n",
" v_reproject.z *= -1\n",
" \n",
" # the Symmetry marker formatting for vectors on the upper and lower hemisphere\n",
" # can be set using `kwargs` and `reproject_scatter_kwargs`, respectively, for\n",
" # Symmetry.plot() \n",
" \n",
" # vectors on the upper hemisphere are shown as open circles\n",
" ax[i].scatter(v, marker='o', fc='None', ec='k', s=150)\n",
" # vectors on the lower hemisphere are reprojected onto the upper hemisphere and\n",
" # shown as crosses\n",
" ax[i].scatter(v_reproject, marker='+', ec='C0', s=150)\n",
"\n",
" ax[i].set_title(f'${s}$ $({sym.name})$')\n",
" ax[i].set_labels('a', 'b', None)\n",
"\n",
"fig.tight_layout()"
]
}
],
"metadata": {
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.7"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
1 change: 1 addition & 0 deletions doc/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ Symmetry
.. currentmodule:: orix.quaternion.Symmetry
.. autosummary::
from_generators
plot
.. autoclass:: orix.quaternion.Symmetry
:show-inheritance:
:members:
Expand Down
69 changes: 69 additions & 0 deletions orix/quaternion/symmetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,75 @@ def fundamental_zone(self):
sr = SphericalRegion(n.unique())
return sr

def plot(
self,
orientation=None,
reproject_scatter_kwargs=None,
**kwargs,
):
"""Stereographic projection of symmetry operations.

The upper hemisphere of the stereographic projection is shown.
Vectors on the lower hemisphere are shown after reprojection
onto the upper hemisphere.

Parameters
----------
orientation : Orientation, optional
The symmetry operations are applied to this orientation
before plotting. The default value uses an orientation
optimized to show symmetry elements.
reproject_scatter_kwargs: dict, optional
Dictionary of keyword arguments for the reprojected scatter
points which is passed to
:meth:`~orix.plot.StereographicPlot.scatter`, which passes
these on to :meth:`matplotlib.axes.Axes.scatter`. The
default marker style for reprojected vectors is "+". Values
used for vector(s) on the visible hemisphere are used unless
another value is passed here.
kwargs : dict, optional
Keyword arguments passed to
:meth:`~orix.plot.StereographicPlot.scatter`, which passes
these on to :meth:`matplotlib.axes.Axes.scatter`.

Returns
-------
fig : matplotlib.figure.Figure
The created figure, returned if `return_figure` is supplied
as a keyword argument and is True.
"""
if orientation is None:
# orientation chosen to mimic stereographic projections as
# shown: http://xrayweb.chem.ou.edu/notes/symmetry.html
orientation = Rotation.from_axes_angles((-1, 8, 1), np.deg2rad(65))
if not isinstance(orientation, Rotation):
raise TypeError("Orientation must be a Rotation instance.")
orientation = self.outer(orientation)

kwargs.setdefault("return_figure", False)
return_figure = kwargs.pop("return_figure")

if reproject_scatter_kwargs is None:
reproject_scatter_kwargs = {}
reproject_scatter_kwargs.setdefault("marker", "+")
reproject_scatter_kwargs.setdefault("label", "lower")

v = orientation * Vector3d.zvector()

figure = v.scatter(
return_figure=True,
axes_labels=("a", "b", None),
label="upper",
reproject=True,
reproject_scatter_kwargs=reproject_scatter_kwargs,
**kwargs,
)
# add symmetry name to figure title
figure.suptitle(f"${self.name}$")

if return_figure:
return figure


# Triclinic
C1 = Symmetry((1, 0, 0, 0))
Expand Down
28 changes: 28 additions & 0 deletions orix/tests/quaternion/test_symmetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from copy import deepcopy

from diffpy.structure.spacegroups import GetSpaceGroup
from matplotlib import pyplot as plt
import numpy as np
import pytest

Expand Down Expand Up @@ -406,6 +407,33 @@ def test_hash_persistence():
assert all(h1a == h2a for h1a, h2a in zip(h1, h2))


@pytest.mark.parametrize("symmetry", [C1, C4, Oh])
def test_symmetry_plot(symmetry):
figure = symmetry.plot(return_figure=True)
assert isinstance(figure, plt.Figure)
assert len(figure.axes) == 1
ax = figure.axes[0]
num = 1 if symmetry.is_proper else 2
assert len(ax.collections) == num
c0 = ax.collections[0]
assert len(c0.get_offsets()) == np.count_nonzero(~symmetry.improper)
assert c0.get_label().lower() == "upper"
if num > 1:
c1 = ax.collections[1]
assert len(c1.get_offsets()) == np.count_nonzero(symmetry.improper)
assert c1.get_label().lower() == "lower"
assert len(ax.texts) == 2
assert ax.texts[0].get_text() == "a"
assert ax.texts[1].get_text() == "b"
plt.close("all")


@pytest.mark.parametrize("symmetry", [C1, C4, Oh])
def test_symmetry_plot_raises(symmetry):
with pytest.raises(TypeError, match="Orientation must be a Rotation instance"):
_ = symmetry.plot(return_figure=True, orientation="test")


class TestFundamentalSectorFromSymmetry:
"""Test the normals, vertices and centers of the fundamental sector
for all 32 crystallographic point groups.
Expand Down