Skip to content

Conversation

@argerlt
Copy link
Collaborator

@argerlt argerlt commented Jul 1, 2025

Description of the change

This started as an update to the plot_symmetry_operations example, which has a few errors and also does not match the symbol conventions in the International ables of Crystallography (ITOC).

I decided to procedurally generate plots for all the point groups (most the hard work was already done, thank you @hakonanes for that), and in the process noticed orix.quaternion.symmetry.C2v was inconsistently defined compared to the other groups as well as ITOC. the common convention is to place the main cyclic rotation group around the z-axis.

In hunting that down, I also noticed some of the symmetry group names also did not match the ITOC names, and some other very minor inconsistencies in naming conventions, so I cleaned them up along with some other functions. I also added some descriptions to existing functions to help with clairity.

orix.quaternion.symmetry should be much more closely aligned with ITOC once this is pushed. Additionally, the following picture shows the auto-generatred stereographic plots for every point group

image

Progress of the PR

Minimal example of the bug fix or new feature

see orix/examples/stereographic_projection/plot_symmetry_operations.py and the plot above.

For reviewers

  • The PR title is short, concise, and will make sense 1 year later.
  • New functions are imported in corresponding __init__.py.
  • New features, API changes, and deprecations are mentioned in the unreleased
    section in CHANGELOG.rst.
  • Contributor(s) are listed correctly in __credits__ in orix/__init__.py and in
    .zenodo.json.

argerlt added 4 commits June 30, 2025 11:41
see chapter 12, as wel as figure 10.3.2 of International Tables of Crystallography, Volume A, for details on this discrepency, but The Cyclic rotation axis should be the z axis, not the x axis, in the standard form.
@argerlt argerlt added this to the v0.14.0 milestone Jul 1, 2025
@argerlt argerlt requested review from CSSFrancis and hakonanes July 1, 2025 00:54
@argerlt argerlt added bug Something isn't working enhancement New feature or request status: needs-review This pull request needs a maintainer's review labels Jul 1, 2025
@argerlt argerlt added the documentation-examples Consider making into a documentation example label Jul 1, 2025
@hakonanes
Copy link
Member

Great job, the plots are very nice! It will take me some time to review, so I cannot say when I'm done.

Copy link
Member

@hakonanes hakonanes left a comment

Choose a reason for hiding this comment

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

Very good job. I have some suggestions which I think should improve maintainability and the user experience.


def __init__(
self,
v: Union[Vector3d, np.ndarray, list, tuple],
Copy link
Member

Choose a reason for hiding this comment

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

Now that 3.10 is the minimal Python version, we should use Vector3d | np.ndarray | list | tuple instead of Union, List, Tuple etc.

v: Union[Vector3d, np.ndarray, list, tuple],
size: int = 1,
folds=2,
inner="fill",
Copy link
Member

Choose a reason for hiding this comment

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

Use inner: Literal["fill", "dot", "half"] = "fill" instead.

Copy link
Collaborator Author

@argerlt argerlt Jul 3, 2025

Choose a reason for hiding this comment

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

Looking back, this variable name and the options reflect the naming used for multi-colored markers in matplotlib, but don't really make sense here. Thus, I changed them to
modifier: Literal["none", "roto", "inv"] = "none") which i think is probably more clear

Copy link
Member

Choose a reason for hiding this comment

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

Why "none" and not None? And I'd use "rotation" and "inversion" as options. Not much more to type but more clear.

Copy link
Collaborator Author

@argerlt argerlt Jul 4, 2025

Choose a reason for hiding this comment

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

Fixed

self,
v: Union[Vector3d, np.ndarray, list, tuple],
size: int = 1,
folds=2,
Copy link
Member

Choose a reason for hiding this comment

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

Use folds: Literal[1, 2, 3, 4, 6] = 2


class SymmetryMarker:
"""
Symmetry element markers to plot in stereographic representations of
Copy link
Member

Choose a reason for hiding this comment

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

Some suggestions to align this docstring with the orix code style:

  • Start description on first line, not second like here
  • Remove type hints in parameter list
  • Remove blank lines between parameters in parameter list
  • Start sentences with an uppercase letter

Parameters
----------
v (Vector3d object)
Vector3d object used for placing marker in StereographicPlot
Copy link
Member

Choose a reason for hiding this comment

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

In general I prefer to make documentation "less technical" as long as no meaning is lost. Here I'd write "Vectors giving the positions of markers in a stereographic plot."

# NOTE: ORIX uses Schoenflies symbols to define point groups. This is partly
# because the notation is short and always starts with a letter (ie, they
# make convenient python variables), and partly because it helps limit
# accidental misinterpretation of of Herman-Mauguin symbols as space group
Copy link
Member

Choose a reason for hiding this comment

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

Remove extra "of"

# fmt: on
_proper_groups = [C1, C2, C2x, C2y, C2z, D2, C4, D4, C3, D3x, D3y, D3, C6, D6, T, O]

def get_point_groups(subset: str = "unique"):
Copy link
Member

Choose a reason for hiding this comment

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

Nice function!

But, at this point, I suggest we make a class just for easy lookup, PointGroups(subset). This can then be used to fetch any symmetry, like PointGroups().get("m-3m") -> Symmetry. What do you think? I think it is easier to work with for users, and also more maintainable than a large function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm having troubles seeing your vision, but I'm always down to get rid of a big ugly lookup function.

Can you lay out a super basic skeleton of the class you are suggesting? I'm guessing it's something like this but maybe you have something else in mind:

class PointGroups()
    def __init__(name: str |List(str))
    -give PG names as strings, returns list of PGs
    def get (name: str |List(str))
    - classmethod that runs get_point_group_from_space_group and returns PGs
    def subset(str)
    - classmethod that returns a subset list 

Copy link
Member

Choose a reason for hiding this comment

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

I like the idea, but don't know how much I like the syntax?

What if you made the _PointGroups class private and then you could

from orix import PointGroups

Which would then be an object

PointGroups.get("m3m")

The extra constructor is weird for essentially a static object right?

Copy link
Member

Choose a reason for hiding this comment

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

My suggestion was simply to change @argerlt's function into a class. The class has a constructor that takes one or more subsets (all, Laue, crystal system, etc.), provides all the relevant properties like HM, Schoenflies, Laue class, crystal system etc., can list available point groups in table (string representation), and extract a point group from the list.

E.g., if we want to plot the IPF color key for all Laue classes, we create a table of those point groups only and iterate over them via their HM symbol.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What if you made the _PointGroups class private and then you could

from orix import PointGroups

Which would then be an object

PointGroups.get("m3m")

The extra constructor is weird for essentially a static object right?

@CSSFrancis ,forgot to mention it, but I liked the idea of a get function a lot and ended up doing a variation of this suggestion.

I still need to finish all of @hakonanes rewview requests, but when I'm done I can ping you and see what you think as well. In short, I think it would be nice to tie together everything related to creating, calling, or looking up crystallographic point groups into a single python class, but I want to make sure what I'm doing makes sense for everyone else's use cases as well.

in "all", of which the other lists are subsets of.
+-------------+--------------+---------------+------------+------------+
Copy link
Member

Choose a reason for hiding this comment

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

With a class, we can print this string representation in the PointGroups.__repr__(). Documentation in code!

@argerlt
Copy link
Collaborator Author

argerlt commented Jul 3, 2025

I think I've tentatively resolved everything except the following two:
https://github.com/pyxem/orix/pull/563/files#r2182622890
https://github.com/pyxem/orix/pull/563/files#r2182562898

These are both going to require more more thought a feedback.

@argerlt argerlt added status: waiting-for-author This pull request has comments the author must address and removed status: needs-review This pull request needs a maintainer's review labels Jul 7, 2025
@argerlt argerlt marked this pull request as draft July 8, 2025 01:16
@argerlt
Copy link
Collaborator Author

argerlt commented Jul 8, 2025

@hakonanes and/or @CSSFrancis , recent realization: Symmetry.plot doesn't do what I think we think it does.

I assumed it would make a stereographic plot of symmetry elements. What it actually does is make a z-axis pole figure.

This doesn't make sense to have as a function of the Symmetry class, so I think it should be removed.

Do you think it is fine to just overwrite it with this new Symmetry.plot? Or, should I depreciate Symmetry.plot and call this new function I am writing Symmetry.plot_elements?

I'd rather do it the first way, but there is a (very unlikely) world where that might break something for someone downstream

Example:

from orix.quaternion import Orientation
from orix.quaternion.symmetry import Oh
import matplotlib.pyplot as plt
from orix.vector import Vector3d

# generate some random orientations
oris = Orientation.random(10, Oh)

# run symmetry.plot
Oh.plot(oris)

# Compare that image to a  <100> pole figure
fig, ax = plt.subplots(subplot_kw={'projection':"stereographic"})
ax.scatter(oris.symmetry.outer(oris*Vector3d.zvector()))

image

@argerlt argerlt marked this pull request as draft July 17, 2025 20:45
@argerlt argerlt removed the status: needs-review This pull request needs a maintainer's review label Jul 17, 2025
@argerlt
Copy link
Collaborator Author

argerlt commented Jul 18, 2025

Okay, I believe I have addressed all review comments and everything is covered and passing.

However, this has gotten rather large and hectic, and there are a lot of PR's right now, I'm going to wait until #555 #558, and #575 are merged before looking at this again.

@argerlt
Copy link
Collaborator Author

argerlt commented Jul 18, 2025

Also, the new plots look significantly nicer, in my opinion
image

image image

@hakonanes
Copy link
Member

@argerlt, are you OK with me fixing the merge conflicts and going over what you've done in a few commits pushed to your branch? I could also just fix conflicts and then make a PR to your branch, which we can then finally merge into orix in this PR?

@argerlt
Copy link
Collaborator Author

argerlt commented Sep 29, 2025

@argerlt, are you OK with me fixing the merge conflicts and going over what you've done in a few commits pushed to your branch? I could also just fix conflicts and then make a PR to your branch, which we can then finally merge into orix in this PR?

I think it might be better if I break this monster out into a 2 smaller and cleaner new PRs, just to avoid polluting the commit history. After that though, feel free to push whatever commits you want to fix things, I think we are more or less in agreement on all the big points.

I was hoping to start this today, after I finished reviewing #588. That said, if tomorrow rolls around and I haven't done anything, feel free to just fix this PR instead.

@hakonanes
Copy link
Member

No problem leaving it for a few days! I'm not sure you'd pollute our git history with this PR... Given the limited resources we have for this project, I'm fine not thinking too much about that.

@hakonanes hakonanes removed this from the v0.14.0 milestone Oct 1, 2025
@hakonanes hakonanes mentioned this pull request Oct 1, 2025
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation-examples Consider making into a documentation example enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automatic plotting of symmetry elements for a point group (Symmetry) in the stereographic projection

4 participants