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

Change left/right symmetry in reduction of (mis)orientations to fundamental zone #442

Draft
wants to merge 23 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a85ded9
Replace some AxAngle from_axes_angles() with equivalent Rotation
hakonanes Apr 5, 2023
f48ba0f
Simplify some Matplotlib calls and messages
hakonanes Apr 5, 2023
c85434d
Separate Orientation and Misorientation tests
hakonanes Apr 5, 2023
c14c575
Restructure some tests into classes, add repr(Object3d) test
hakonanes Apr 5, 2023
7917b06
Add classmethod to get vectors between two or more vectors
hakonanes Apr 6, 2023
fb1845a
Ensure polar stereographic grid uses Matplotlib's grid.alpha
hakonanes Apr 6, 2023
c1654e2
Add doc example showing how to combine rotations
hakonanes Apr 6, 2023
febf873
Re-run orientation clustering notebook following change in reduce
hakonanes Apr 6, 2023
d13f12f
Remove broken link to pymicro docs in "Related projects"
hakonanes Apr 6, 2023
1667030
Update clustering notebooks following change in reduction to FZ
hakonanes Apr 6, 2023
0d6a427
Minor improvements to docs
hakonanes Apr 6, 2023
bf00d03
Re-run orientation clustering tutorial following FZ reduction change
hakonanes Apr 6, 2023
4261d66
Deprecate map_into_sym...() in favor of reduce()
hakonanes Apr 6, 2023
e0e7929
Update tests following deprecation of map_into_sym...()
hakonanes Apr 6, 2023
e80192e
Use fundamental zone reduce() in tutorials
hakonanes Apr 6, 2023
57ebc78
Expedite removal of convention parameter from 1.0 to 0.13
hakonanes Apr 6, 2023
e836e27
Use private _symmetry to simplify plotting in fundamental zone
hakonanes Apr 7, 2023
7c86393
Group tests of reduction to FZ, add test of smallest angle
hakonanes Apr 7, 2023
b1ca31a
Explain need of negative angle in axis-angle representation
hakonanes Apr 7, 2023
b6006c4
Fix rst math formatting in get_path()
hakonanes Apr 8, 2023
373dcc0
Improve readability of orientation region tests
hakonanes Apr 8, 2023
0f5e68d
Re-introduce description of orientation region in docs
hakonanes Apr 8, 2023
73ed872
Add test of vector coordinates returned from rotation plots
hakonanes Apr 8, 2023
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
12 changes: 12 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,30 @@ Unreleased

Added
-----
- ``Vector3d.get_path()`` class method to get vectors between two or more vectors.

Changed
-------
- The ``reduce()`` method (replacing ``map_into_symmetry_reduced_zone()``) for
the ``Misorientation`` and ``Orientation`` classes now apply symmetry operators in the
opposite order of what was done previously.
- The ``convention`` parameter in ``from_euler()`` and ``to_euler()`` will be removed in
the next minor release, 0.13, instead of release 1.0 as previously stated.

Deprecated
----------
- ``map_into_symmetry_reduced_zone()`` is deprecated since 0.12 and will be removed in
0.13. Use ``reduce()`` instead.

Removed
-------
- ``verbose`` parameter in ``reduce()``
(replacing ``map_into_symmetry_reduced_zone()``).

Fixed
-----
- Transparency of polar stereographic grid lines can now be controlled by Matplotlib's
``grid.alpha``, just like the azimuth grid lines.

2023-03-14 - version 0.11.1
===========================
Expand Down
1 change: 0 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
"nbval": ("https://nbval.readthedocs.io/en/latest", None),
"numpy": ("https://numpy.org/doc/stable", None),
"numpydoc": ("https://numpydoc.readthedocs.io/en/latest", None),
"pymicro": ("https://pymicro.readthedocs.io/projects/pymicro/en/latest", None),
"pytest": ("https://docs.pytest.org/en/stable", None),
"python": ("https://docs.python.org/3", None),
"pyxem": ("https://pyxem.readthedocs.io/en/latest", None),
Expand Down
26 changes: 10 additions & 16 deletions doc/tutorials/clustering_across_fundamental_region_boundaries.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,21 @@
"cluster1 = Rotation.random_vonmises(n_orientations, alpha=alpha)\n",
"\n",
"# Cluster 2\n",
"centre2 = Rotation.from_axes_angles((1, 0, 0), np.pi / 4)\n",
"centre2 = Rotation.from_axes_angles((1, 0, 0), -45, degrees=True)\n",
"cluster2 = Rotation.random_vonmises(\n",
" n_orientations, alpha=alpha, reference=centre2\n",
")\n",
"\n",
"# Cluster 3\n",
"centre3 = Rotation.from_axes_angles((1, 1, 0), np.pi / 3)\n",
"centre3 = Rotation.from_axes_angles((1, 1, 0), -60, degrees=True)\n",
"cluster3 = Rotation.random_vonmises(\n",
" n_orientations, alpha=alpha, reference=centre3\n",
")\n",
"\n",
"# Stack and map into the Oh fundamental zone\n",
"ori = Orientation.stack([cluster1, cluster2, cluster3]).flatten()\n",
"ori.symmetry = Oh\n",
"ori = ori.map_into_symmetry_reduced_zone()"
"ori = ori.reduce()"
]
},
{
Expand All @@ -102,7 +102,7 @@
"\n",
"### Perform clustering without application of crystal symmetry\n",
"\n",
"Compute misorientations, i.e. distance between orientations"
"Compute misorientation angles, i.e. distance between orientations, without symmetry"
]
},
{
Expand All @@ -116,11 +116,8 @@
"# Remove symmetry by setting it to point group 1 (identity operation)\n",
"ori_without_symmetry = Orientation(ori.data, symmetry=C1)\n",
"\n",
"# Misorientations\n",
"mori1 = (~ori_without_symmetry).outer(ori_without_symmetry)\n",
"\n",
"# Misorientation angles\n",
"D1 = mori1.angle"
"D1 = ori_without_symmetry.angle_with_outer(ori_without_symmetry)"
]
},
{
Expand All @@ -136,6 +133,7 @@
"metadata": {},
"outputs": [],
"source": [
"D1 = D1.astype(np.float32)\n",
"dbscan_naive = DBSCAN(eps=0.3, min_samples=10, metric=\"precomputed\").fit(D1)\n",
"print(\"Labels:\", np.unique(dbscan_naive.labels_))"
]
Expand All @@ -155,12 +153,7 @@
"metadata": {},
"outputs": [],
"source": [
"mori2 = (~ori).outer(ori)\n",
"\n",
"mori2.symmetry = Oh\n",
"mori2 = mori2.map_into_symmetry_reduced_zone()\n",
"\n",
"D2 = mori2.angle"
"D2 = ori.angle_with_outer(ori)"
]
},
{
Expand All @@ -176,9 +169,10 @@
"metadata": {},
"outputs": [],
"source": [
"D2 = D2.astype(np.float32)\n",
"dbscan = DBSCAN(\n",
" eps=np.deg2rad(17), min_samples=20, metric=\"precomputed\"\n",
").fit(D2.astype(np.float32))\n",
").fit(D2)\n",
"print(\"Labels:\", np.unique(dbscan.labels_))"
]
},
Expand Down Expand Up @@ -284,7 +278,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.8"
"version": "3.10.10"
}
},
"nbformat": 4,
Expand Down
83 changes: 33 additions & 50 deletions doc/tutorials/clustering_misorientations.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"\n",
"from orix import data, plot\n",
"from orix.quaternion import Orientation, Misorientation, Rotation\n",
"from orix.quaternion.symmetry import D6\n",
"from orix.vector import Vector3d\n",
"\n",
"\n",
Expand All @@ -52,7 +51,7 @@
"source": [
"## Import data\n",
"\n",
"Load Ti orientations with the point group symmetry *D6* (*622*). We have to explicitly allow downloading from an external source."
"Load Ti orientations with the point group symmetry *622* (*D6*). We have to explicitly allow downloading from an external source."
]
},
{
Expand All @@ -69,7 +68,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The orientations define transformations from the sample (lab) to the crystal reference frame, i.e. the Bunge convention. The above referenced paper assumes the opposite convention, which is the one used in MTEX. So, we have to invert the orientations"
"Extract the symmetry"
]
},
{
Expand All @@ -78,30 +77,14 @@
"metadata": {},
"outputs": [],
"source": [
"ori = ~ori"
"sym = ori.symmetry"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Reshape the orientation mapping data to the correct spatial dimension for the scan"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ori = ori.reshape(381, 507)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Select a subset of the orientations with a suitable size for this demonstration"
"Reshape the orientation mapping data to the correct spatial dimension for the scan and select a subset of the orientations with a suitable size for this demonstration (bottom left corner)"
]
},
{
Expand All @@ -110,6 +93,7 @@
"metadata": {},
"outputs": [],
"source": [
"ori = ori.reshape(381, 507)\n",
"ori = ori[-100:, :200]"
]
},
Expand All @@ -126,16 +110,15 @@
"metadata": {},
"outputs": [],
"source": [
"ckey = plot.IPFColorKeyTSL(D6)\n",
"ckey = plot.IPFColorKeyTSL(sym)\n",
"\n",
"directions = [(1, 0, 0), (0, 1, 0)]\n",
"titles = [\"X\", \"Y\"]\n",
"directions = [(1, 0, 0), (0, 0, 1)]\n",
"titles = [\"X\", \"Z\"]\n",
"\n",
"fig, axes = plt.subplots(ncols=2, figsize=(15, 10))\n",
"for i, ax in enumerate(axes):\n",
" ckey.direction = Vector3d(directions[i])\n",
" # Invert because orix assumes lab2crystal when coloring orientations\n",
" ax.imshow(ckey.orientation2color(~ori))\n",
" ax.imshow(ckey.orientation2color(ori))\n",
" ax.set_title(f\"IPF-{titles[i]}\")\n",
" ax.axis(\"off\")\n",
"\n",
Expand All @@ -154,7 +137,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Map the orientations into the fundamental zone (find symmetrically equivalent orientations with the smallest angle of rotation) of *D6*"
"Map the orientations into the Rodrigues fundamental zone (find symmetrically equivalent orientations with the smallest angle of rotation) of *622*"
]
},
{
Expand All @@ -163,14 +146,14 @@
"metadata": {},
"outputs": [],
"source": [
"ori = ori.map_into_symmetry_reduced_zone()"
"ori = ori.reduce()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Compute misorientations (in the horizontal direction)"
"Compute misorientations $m_{AB} = g_B \\cdot g_A^{-1}$ (in the horizontal direction)"
]
},
{
Expand All @@ -179,7 +162,7 @@
"metadata": {},
"outputs": [],
"source": [
"mori_all = Misorientation(~ori[:, :-1] * ori[:, 1:])"
"mori_all = Misorientation(ori[:, :-1] * ~ori[:, 1:], symmetry=(sym, sym))"
]
},
{
Expand All @@ -203,7 +186,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Map the misorientations into the fundamental zone of (*D6*, *D6*)"
"Map the misorientations into the Mackenzie fundamental zone of *622-622*"
]
},
{
Expand All @@ -212,8 +195,7 @@
"metadata": {},
"outputs": [],
"source": [
"mori.symmetry = (D6, D6)\n",
"mori = mori.map_into_symmetry_reduced_zone()"
"mori = mori.reduce()"
]
},
{
Expand Down Expand Up @@ -267,6 +249,7 @@
"outputs": [],
"source": [
"# Compute clusters\n",
"D = D.astype(np.float32)\n",
"dbscan = DBSCAN(eps=0.05, min_samples=10, metric=\"precomputed\").fit(D)\n",
"\n",
"unique_labels, all_cluster_sizes = np.unique(\n",
Expand All @@ -291,9 +274,7 @@
"metadata": {},
"outputs": [],
"source": [
"unique_cluster_labels = unique_labels[\n",
" 1:\n",
"] # Without the \"no-cluster\" label -1\n",
"unique_cluster_labels = unique_labels[1:] # Drop \"no-cluster\" label -1\n",
"cluster_sizes = all_cluster_sizes[1:]\n",
"\n",
"rc = Rotation.from_axes_angles((0, 0, 1), 15, degrees=True)\n",
Expand All @@ -304,21 +285,21 @@
" mori_i = rc * mori[dbscan.labels_ == label]\n",
"\n",
" # Map into the fundamental zone\n",
" mori_i.symmetry = (D6, D6)\n",
" mori_i = mori_i.map_into_symmetry_reduced_zone()\n",
" mori_i.symmetry = (sym, sym)\n",
" mori_i = mori_i.reduce()\n",
"\n",
" # Get the cluster mean\n",
" mori_i = mori_i.mean()\n",
"\n",
" # Rotate back and add to list\n",
" cluster_mean_local = (~rc) * mori_i\n",
" cluster_mean_local = ~rc * mori_i\n",
" mori_mean.append(cluster_mean_local)\n",
"\n",
"cluster_means = Misorientation.stack(mori_mean).flatten()\n",
"\n",
"# Map into the fundamental zone\n",
"cluster_means.symmetry = (D6, D6)\n",
"cluster_means = cluster_means.map_into_symmetry_reduced_zone()\n",
"cluster_means.symmetry = (sym, sym)\n",
"cluster_means = cluster_means.reduce()\n",
"cluster_means"
]
},
Expand Down Expand Up @@ -378,7 +359,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Calculate difference, defined as minimum rotation angle, between measured and theoretical values"
"Calculate difference, defined as minimum rotation angle, between measured and theoretical values.\n",
"This procedure accounts for the edges of the fundamental zone"
]
},
{
Expand All @@ -387,10 +369,10 @@
"metadata": {},
"outputs": [],
"source": [
"mori2 = (~twin_theory).outer(cluster_means)\n",
"sym_op = D6.outer(D6).unique()\n",
"mori2 = twin_theory.outer(cluster_means)\n",
"sym_ops = sym.outer(sym).unique()\n",
"mori2_equiv = (\n",
" D6.outer(~twin_theory).outer(sym_op).outer(cluster_means).outer(D6)\n",
" sym.outer(twin_theory).outer(sym_ops).outer(cluster_means).outer(sym)\n",
")\n",
"D2 = mori2_equiv.angle.min(axis=(0, 2, 4))"
]
Expand Down Expand Up @@ -447,7 +429,7 @@
"cluster_sizes_scaled = 1000 * cluster_sizes / cluster_sizes.max()\n",
"\n",
"fig, ax = plt.subplots(\n",
" figsize=(5, 5), subplot_kw=dict(projection=\"ipf\", symmetry=D6)\n",
" figsize=(5, 5), subplot_kw=dict(projection=\"ipf\", symmetry=sym)\n",
")\n",
"ax.scatter(\n",
" cluster_means.axis, c=colors, s=cluster_sizes_scaled, alpha=0.5, ec=\"k\"\n",
Expand Down Expand Up @@ -559,13 +541,14 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"fig3, ax3 = plt.subplots(figsize=(15, 10))\n",
"ax3.imshow(mapping)\n",
"ax3.set_xticks([])\n",
"ax3.set_yticks([]);"
"ax3.set(xticks=[], yticks=[]);"
]
}
],
Expand All @@ -585,7 +568,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.8"
"version": "3.10.10"
}
},
"nbformat": 4,
Expand Down
Loading