Closed hakonanes closed 2 months ago
Check out this pull request on
See visual diffs & provide feedback on Jupyter Notebooks.
Powered by ReviewNB
The only thing left I'd like to look at is the new gallery example on sampling reduced rotations by @CSSFrancis:
A couple questions:
pg432 = symmetry.O
? How to create a phase is covered in other parts of the documentation, like in the crystal map tutorial (https://orix.readthedocs.io/en/stable/tutorials/crystal_map.html). We can of course expand upon this by adding a Phase
section to the gallery.If it's OK with you @CSSFrancis, I'd like to re-work the example a bit along those lines.
- The only necessary phase information to run the example are the symmetry operations of the proper point group 432. Can we reduce the part with the phase setup to something like
pg432 = symmetry.O
? How to create a phase is covered in other parts of the documentation, like in the crystal map tutorial (https://orix.readthedocs.io/en/stable/tutorials/crystal_map.html). We can of course expand upon this by adding aPhase
section to the gallery.
I think that is fine for the most part but it is sometimes nice to point out how someone can get that information from a Phase
object. At least for people doing orientation mapping I think they are more likely to be working with the Phase
rather than the symmetry.
- In the example, we sample SO(3). But, the plotting only shows S2. We should show the sampled orientations, not just the rotated vectors. Otherwise, if rotated vectors was the goal, I'm wondering as a user why I can't just use the S2 sampling instead. What do you think, @CSSFrancis?
This is also related to the orientation mapping. I guess the reason is that we want the rotations because that is the result of the orientation mapping code. I think this is a good example of converting rotations to vectors for visualization by essentially defining a beam direction. You lose in-plane rotation information but in this case there isn't any. Maybe this is an anti-pattern and I don't understand the orix
code as well as I should....
@hakonanes I don't have a great understanding of orix
but it seems like the S2 sampling could return a constrained rotation based on some vector direction rather than return a vector and that might be more useful in relation to the other parts of orix.
At least for people doing orientation mapping I think they are more likely to be working with the Phase rather than the symmetry.
This is a good point. I've opened #480 to discuss this further.
it seems like the S2 sampling could return a constrained rotation based on some vector direction
This is what you added in get_sample_reduced_fundamental()
, which is great. I just didn't get the motivation behind your added example. Basically, the new function returns rotations which map the vector (0, 0, 1) onto the fundamental sector on the sphere of the corresponding Laue group.
May this then be a more descriptive example?
import matplotlib.pyplot as plt
import numpy as np
from orix import plot, sampling
from orix.quaternion import symmetry
from orix.vector import Vector3d
# Input: sampling resolution and symmetry
res = 2 # Degrees
laue = symmetry.D4h # 4/mmm
R = sampling.get_sample_reduced_fundamental(res, point_group=laue)
# Show equivalence of vectors v1 and v2
v1 = R * Vector3d.zvector()
v2 = sampling.sample_S2(res)
v2 = v2[v2 <= laue.fundamental_sector]
assert np.allclose(v1.data, v2.data)
# True
fig, (ax0, ax1) = plt.subplots(
ncols=2, subplot_kw={"projection": "ipf", "symmetry": pg}, layout="tight"
)
ax0.scatter(v1, s=5)
ax1.scatter(v2, c="C1", s=5)
ax0.set_title("Rotated Z vectors", loc="left")
ax1.set_title("Directly sampled", loc="left")
fig.suptitle("Vectors in the fundamental sector of 4/mmm", y=0.8)
At least for people doing orientation mapping I think they are more likely to be working with the Phase rather than the symmetry.
This is a good point. I've opened #480 to discuss this further.
Perfect!
it seems like the S2 sampling could return a constrained rotation based on some vector direction
This is what you added in
get_sample_reduced_fundamental()
, which is great. I just didn't get the motivation behind your added example. Basically, the new function returns rotations which map the vector (0, 0, 1) onto the fundamental sector on the sphere of the corresponding Laue group.May this then be a more descriptive example?
That is much more descriptive :) I don't think I thought as much as I should about what the example should show.
I've added the above example to the examples gallery: https://orix--478.org.readthedocs.build/en/478/examples/rotations/rotations_mapping_fundamental_sector.html
I've got the following questions regarding the new function get_sample_zone_axis()
:
Phase.point_group.fundamental_sector.vertices
.density="7"
and return_directions=True
are not equidistant from the two (non-unit) vectors that are added. Is this intentional?We need to change this function for it to align with the rest of orix.
I've updated the TODO list in the top comment with what's left before we're ready for the 0.12.0 release.
I've added the above example to the examples gallery: https://orix--478.org.readthedocs.build/en/478/examples/rotations/rotations_mapping_fundamental_sector.html
I've got the following questions regarding the new function
get_sample_zone_axis()
:
- Where are the hard-coded fundamental sector vertices (corners) from? Many of them differ from the vertices we use in orix. We have eleven Laue classes, with a different fundamental sector for each one (not just 6/7 crystal systems, excluding triclinic). For these, only mmm, 4/mmm, and m-3m match. We don't get one for -1 (triclinic), while the remaining are different. I suggest to get the corners from
Phase.point_group.fundamental_sector.vertices
.
I'd imagine that this is a holdover from diffsims
. I just copied what was previously in diffsims but in relation to orix
I think the approach is a bit naive. We should drop the hard coded and just use the fundamental sector. I still think the method to return the Rotations for the fundamental sectors/ zone axes is useful. Usually I like to use this to give examples of high symmetry zone axes to help with aligning on the TEM.
- Adding unit vectors are different than adding non-unit vectors, say (1, 1, 1) + (0, 0, 1) vs. (0.5574, 0.5574, 0.5574) + (0, 0, 1). Thus, the midpoint vectors returned when
density="7"
andreturn_directions=True
are not equidistant from the two (non-unit) vectors that are added. Is this intentional?
This is most likely a bug. And using the fundamental sectors.vectors would be much better.
- And what is the purpose of these midpoint vectors in down-stream packages?
We need to change this function for it to align with the rest of
orix
.
Honestly coming from a non crystallography background I looked in orix
but am not particularly familiar with terms like fundamental sector and fundamental_sector.vecticies
is fairly hidden. So something like Zone Axis, high symmetry axis etc is much more descriptive to me. I'm not sure that there is a difference between the two? But it would be good to have something in the documentation related to Zone Axis or high symmetry which helps those of us with a TEM background. Adding an example like "Getting High Symmetry Axes" would be nice, but maybe the Great Circle Example should have been enough for me :).
Edit: This doesn't make sense... I'm mixing this up a bit. Let me try again
Edit x2: Okay now this makes more sense.
Thank you for clarifying!
Since the get_sample_zone_axis()
function won't be used directly by down-stream packages, I suggest to remove it but add descriptive examples to get the desired result.
The below is one route to some nice high-symmetry zone axes is. If it's OK with you @CSSFrancis, I'll replace the function and corresponding tests with this example.
from orix.crystal_map import Phase
from orix.vector import Miller
phase = Phase(point_group="mmm") # m-3m, 6/mmm, 4/mmm, -3m, mmm, 2/m
t = Miller.from_highest_indices(phase, uvw=[1, 1, 1])
t = t.in_fundamental_sector() # Project all to the fundamental sector
t = t.unit.unique(use_symmetry=True).round() # Unit vectors ensure e.g. [222] == [111]
print(t)
# Miller (7,), point group mmm, uvw
# [[0. 0. 1.]
# [0. 1. 1.]
# [0. 1. 0.]
# [1. 1. 1.]
# [1. 0. 1.]
# [1. 1. 0.]
# [1. 0. 0.]]
Plotting the annotated vectors, only showing the fundamental sector
fig = t.scatter(
vector_labels=[str(vi).replace(".", "") for vi in t.data],
text_kwargs={"size": 15, "offset": (0, 0.02), "bbox": {"fc": "w", "pad": 1, "alpha": 0.75}},
return_figure=True,
)
fig.axes[0].restrict_to_sector(t.phase.point_group.fundamental_sector)
Thank you for clarifying!
Since the
get_sample_zone_axis()
function won't be used directly by down-stream packages, I suggest to remove it but add descriptive examples to get the desired result.The below is one route to some nice high-symmetry zone axes is. If it's OK with you @CSSFrancis, I'll replace the function and corresponding tests with this example.
from orix.crystal_map import Phase from orix.vector import Miller phase = Phase(point_group="mmm") # m-3m, 6/mmm, 4/mmm, -3m, mmm, 2/m t = Miller.from_highest_indices(phase, uvw=[1, 1, 1]) t = t.in_fundamental_sector() # Project all to the fundamental sector t = t.unit.unique(use_symmetry=True).round() # Unit vectors ensure e.g. [222] == [111] print(t) # Miller (7,), point group mmm, uvw # [[0. 0. 1.] # [0. 1. 1.] # [0. 1. 0.] # [1. 1. 1.] # [1. 0. 1.] # [1. 1. 0.] # [1. 0. 0.]]
Plotting the annotated vectors, only showing the fundamental sector
fig = t.scatter( vector_labels=[str(vi).replace(".", "") for vi in t.data], text_kwargs={"size": 15, "offset": (0, 0.02), "bbox": {"fc": "w", "pad": 1, "alpha": 0.75}}, return_figure=True, ) fig.axes[0].restrict_to_sector(t.phase.point_group.fundamental_sector)
I think that is useful but it would be nice to include an example the returns the Rotations as well as the vectors for simulation purposes.
I think a workflow like:
import diffsims as ds
from orix.crystal_map import Phase
phase = Phase.from_cif("strucuture.cif")
sim = ds.simulations.Simulation()
zone_axes_patterns = sim.get_zone_axes_patterns(phase)
is something we should be able to do fairly easily. If this is only used in diffsims
then no reason to have a separate function but I suspect that this is a general enough function that others might be interested in it.
I think something like the ASTAR/ JEMS GUI is something that we could fairly easily replicate with orix/diffsims and it would be very useful to a lot of people.
it would be nice to include an example the returns the Rotations as well as the vectors for simulation purposes.
I've added the above example with an additional section showing how to get the rotations rotating the Z-vector (0, 0, 1) onto the high-symmetry crystal directions.
To have the syntax you suggest in diffsims, I suggest to make a function there instead of in orix. My feeling is that orix should be free of diffraction-specific functionality and stick to crystal orientations and directions. It may be that a more specific class method could be added to the ReciprocalLatticeVector
class. This class is orix' Miller(hkl)
class specifically tailored toward diffraction.
@CSSFrancis, I'd appreciate it if you could have a look at the new examples, the rephrased docstring of get_sample_reduced_fundamental()
, and the reworked test of that function.
It's probably not super simple as the align vectors can handle the case where you are trying to align multiple vectors but we could also just add the function Rotation.from_align_vector which assumes a direct rotation from one vector to another.
Good point. Well, in the API, we only support the case where the two end point vectors have the same size. So, we could just say that when the sizes differ, we do broadcasting, instead of raising an error. In the simplest case, we can support 1-to-n and n-to-1. It may be slow, since the SciPy function we call does not support broadcasting, but it is possible.
LGTM! @hakonanes You can merge if you would like.
Description of the change
This PR contains a few maintenance updates before the 0.12.0 release.
In particular, one test,
test_restrict_to_fundamental_sector()
, failed on my Mac with Python 3.11 and Matplotlib 3.8.3. The relevant part of the test ensures that the edges of the fundamental sectors of C1 and C6 are different, but failed because a different number of edges were returned. I'm not sure why. Anyways, to ensure the edges are different, we only need to check the first couple edge vectors. I've made it so we check the first ten.Other changes:
pytest-rerunfailures
on GitHub CI. Package added as a test dependency.Progress of the PR
get_sample_zone_axis()
get_sample_zone_axis()
For reviewers
__init__.py
.CHANGELOG.rst
.__credits__
inorix/__init__.py
and in.zenodo.json
.