Skip to content

Conversation

@weisscharlesj
Copy link
Contributor

This is still being tested and polished, but I'm interested in some feedback. This pull request adds functionality for chemical applications of symmetry/group theory including the ability to:

  • Decompose reducible representations into irreducible representations
  • Calculate reducible representations from number of irreducible representations (reverse of above)
  • Determine vibrational modes from reducible representations
  • Calculate IR/Raman active modes from reducible representations
  • Predict symmetry adapted linear combination of orbitals (SALCs) by the projection operator method
  • Predict SALCs using symmetry functions in character tables

For example, a reducible representation can be created and decomposed into the number of each irreducible representations for the point group by the following.

rep = Reducible([15, 0, 0, 7, -2, -2], 'c3h’, vibe_only=True)
rep.dcomp_red()
array([3, 4, 1, 1])

Reducible representations composed of all motions (i.e., rotation, vibration, and translation) can be relatively easily be created by providing the from_atoms() method with the number of atoms that remain stationary during each symmetry operation for the molecule’s point group. For example, water would be as follows.

rep = Reducible.from_atoms([3, 1, 3, 1], 'c2v')
rep
array([9, -1, 3, 1])

This can be decomposed into irreducible representations.

rep.decom_red()
array([3, 1, 3, 2])

A reducible can also be used to predict IR or Raman active modes such as below. This means this representation contains two A1 and one B1 IR active mode, for example.

rep.ir_active()
array([2, 0, 1, 0])

rep.raman_active()
array([2, 0, 1, 0])

There are also functions provides to help the user know the order the symmetry operations and irreducible representations (Mulliken symbols) are provided in when given the point group Schoenflies notation.

get_header(‘c2v’)
E C₂ σv σvʹ

get_mulliken('c2v')
A1 A2 B1 B2

The second piece of the is pull request is salcs.py which generates the symmetry adapted linear combinations (SALCs) of molecular orbitals using the projection operator method. I’m not as happy with this piece. It works fine except still doesn’t generate all the SALCS for degenerate irreducible representations. Most inorganic chemistry text books dodge this point, so I’m still searching for a systematic solutions to generate degenerate SALCS. An alternate method for generating SALCS is also provided using the symmetry functions found on the right side of the character table. The user just needs to provide the xyz coordinate for the outer atoms or the angles.

Add chemical applications of symmetry/group theory functionality including the ability to decompose reducible representations into irreducible representations,  calculate IR/Raman active modes, predict symmetry adapted linear combination of orbitals (SALCs) by the projection operator method, etc.
Fix representations.py documentation by updating function names in examples.
@bjodah
Copy link
Owner

bjodah commented Jun 3, 2025

Hi @weisscharlesj, sorry for the delay.
I've look through your code and it looks good and comprehensive. I'd like to do a proper in-depth review, but I would need to brush up my knowledge about point groups, symmetries, etc. Unfortunately, for the next couple of months I will not have the time to do so properly. If someone else from the community feel that they have the required expertise to review this, please go ahead and do so! (that way, we might be able to merge this in a more attractive time frame).

I appreciate your time and work in submitting this pull request, and I apologize that the turn-around time is so long. I'm just curious: are you using ChemPy yourself in teaching or research?

@weisscharlesj
Copy link
Contributor Author

Sounds good. I should have an update to this PR probably before next week once I finish adding the ability to return dictionaries and more tests for the SALCs functionality.

I use ChemPy a bit in my teaching such as my Scientific Computing for Chemists course. I hope to add some ChemPy examples to my open computing textbook before I teach this course again.

Add the ability for the user to return results as a dictionary with mulliken symbols as keys for easier interpretation. Updated documentation, fixed a series of minor issues, added more tests, and improved consistency across functions.
@weisscharlesj
Copy link
Contributor Author

Here is an updated PR with all the features I intend to implement. A few changes were made for user convenience such as the ability to return dictionaries with mulliken symbols as keys. Here is a summary of all the key features.

representations.py

  • Decompose reducible representations into the number of each type of irreducible representation for that point group.
    >>> water = Reducible([9, -1, 3, 1], 'c2v', all_motion=True)
    >>> water.decomp()
    >>> array([3, 1, 3, 2])
    >>> water.decomp(to_dict=True)
    >>> {'A1': 3, 'A2': 1, 'B1': 3, 'B2': 2}
  • Calculate vibrational, IR, and Raman active modes.
    >>> water.vibe_modes()
    >>> array([2, 0, 1, 0])
    >>> water.ir_active()
    >>> array([2, 0, 1, 0])
    >>> water.raman_active(to_dict=True)
    >>> {'A1': 2, 'A2': 0, 'B1': 1, 'B2': 0}
  • Generate reducible representations (with all motions: rotational, translational, and vibrational) based on the user inputting the number of stationary atoms for each symmetry operation. The below example is for trans-1,2-dichloroethylene.
    >>> rep = Reducible.from_atoms([6, 0, 0, 6], 'c2h')
    >>> rep.gamma
    >>> array([18, 0, 0, 6])

salcs.py

  • Return the symmetry adapted linear combinations (SALCs) using the projection operator method - results are returned as SymPy variables provided by the user to designate each outer atom atomic or ligand orbital. This is the conventional method taught in inorganic chemistry but requires a considerable amount of arithmetic if done by hand. This feature aims to help make this easier. The example below is for the hydrogen 1s orbitals of ammonia. The one shortcoming is that it still doesn't return multiple SALCs for doubly and triply degenerate irreducible representations (any ideas?).
    >>> a, b, c = sympy.symbols('a b c')
    >>> calc_salcs_projection([a, b, c, a, b, c], 'c3v')
    >>> [2*a + 2*b + 2*c, 0, 2*a - b - c]
    >>> calc_salcs_projection([a, b, c, a, b, c], 'c3v', to_dict=True)
    >>> {'A1': 2*a + 2*b + 2*c, 'A2': 0, 'E': 2*a - b - c}
  • Return the SALCs using the position of the outer atoms or ligands and the symmetry functions for each irreducible representation (right side of character tables). This is a less conventional method but can be considerably easier for point groups with many symmetry operations. The user needs to provide the location of the outer atoms either as xyz coordinates or angles (azimuth and elevation). This approach also returns multiple SALCs for doubly and triply degenerate irreducible representations, which is an advantage over the projection operator method above. The example below is for the ligands of an octahedral complex with the first example providing the function with angles and the second example with xyz coordinates.
    >>> a, b, c, d, e, f = sympy.symbols('a b c d e f')
    >>> calc_salcs_func([[0, 0], [90, 0], [180, 0], [270, 0], [0, 90], [0, -90]], 'oh', [a, b, c, d, e, f], mode='angle')
    >>> [a + b + c + d + e + f, 0, [-a - b - c - d + 2*e + 2*f, a - b + c - d], 0, 0, 0, 0, 0, [a - c, b - d, e - f], 0]
    >>> calc_salcs_func([[1, 0, 0], [0, 1, 0], [-1, 0, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]], 'oh', [a, b, c, d, e, f])
    >>> [a + b + c + d + e + f, 0, [-a - b - c - d + 2*e + 2*f, a - b + c - d], 0, 0, 0, 0, 0, [a - c, b - d, e - f], 0]
    >>> calc_salcs_func([[1, 0, 0], [0, 1, 0], [-1, 0, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]], 'oh', [a, b, c, d, e, f], to_dict=True)
    >>> {'A1g': a + b + c + d + e + f, 'A2g': 0, 'Eg': [-a - b - c - d + 2*e + 2*f, a - b + c - d], 'T1g': 0, 'T2g': 0, 'A1u': 0, 'A2u': 0, 'Eu': 0, 'T1u': [a - c, b - d, e - f], 'T2u': 0}

Fixed a few typos in tables.py, reformatted the dictionaries for easier reading/proof reading, and added more comments and a reference. Added more tests to test_representations.py to cover a wider set of point groups along with more tests for generating reducible representations from stationary atoms.
Copy link
Owner

@bjodah bjodah left a comment

Choose a reason for hiding this comment

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

I have made some effort to make the master branch of ChemPy play nicely with latest NumPy (>2) and Python, and I intend to mint a new release in the upcoming weeks. This PR looks really good, so I'm hoping to merge it before that happens. I will not review the technical details (I trust that you got things right, otherwise we can always fix any mistakes later on).

I left some comments throughout, let me know what you think.

Renamed get_xyz() functions to print_xyz() and corrected Schoenflies misspellings.
Changed tables dictionary in tables.py to use SymPy numeric representations for non-integer values to make calculations for exact. Added sympy_to_num() function in representations.py to evaluate SymPy representations to floats when necessary for calculations (e.g., decomp()).
Added a print_table() function that prints a decent-looking character table for the user. This requires the tabulate library, which adds an extra ChemPy dependency. I can remove this function if you want to avoid another dependency.
Updated calc_salc_func() to accept angles in the spherical coordinate convention to be more consistent with the convention used by SymPy and SciPy. Updated tests and documentation accordingly.
@weisscharlesj
Copy link
Contributor Author

I believe this commit resolves all the requests along with adjusting the spherical coordinate convention to be consistent with SciPy and Sympy, and some minor edits for clarity. I added a print_table() function that uses tabulate. I’m fine removing this function if you'd prefer to avoid this dependency.

@bjodah
Copy link
Owner

bjodah commented Sep 2, 2025

Looking good!

I merged master branch, added tabulate to the dependencies, and then I added some ignores for flake8 in commit 0b4aae5 and e2b5859 (the "violations" with largest number of occurrences).

The remaining ones are these:

$ python -m flake8 chempy/symmetry/
chempy/symmetry/representations.py:66:1: E302 expected 2 blank lines, found 1
chempy/symmetry/representations.py:83:1: E302 expected 2 blank lines, found 1
chempy/symmetry/representations.py:116:1: E302 expected 2 blank lines, found 1
chempy/symmetry/representations.py:147:1: E302 expected 2 blank lines, found 1
chempy/symmetry/salcs.py:14:1: E402 module level import not at top of file
chempy/symmetry/salcs.py:21:1: E302 expected 2 blank lines, found 1
chempy/symmetry/tables.py:4:1: F401 'cmath' imported but unused
chempy/symmetry/tables.py:4:1: F401 'math' imported but unused
chempy/symmetry/tables.py:4:13: E401 multiple imports on one line
chempy/symmetry/tables.py:729:14: E128 continuation line under-indented for visual indent
chempy/symmetry/tests/test_representations.py:41:1: E305 expected 2 blank lines after class or function definition, found 1
chempy/symmetry/tests/test_representations.py:53:1: E302 expected 2 blank lines, found 1
chempy/symmetry/tests/test_representations.py:112:5: E303 too many blank lines (2)
chempy/symmetry/tests/test_salcs.py:4:1: F401 '..salcs._expand_irreducible' imported but unused
chempy/symmetry/tests/test_salcs.py:4:1: F401 '..salcs._angles_to_vectors' imported but unused
chempy/symmetry/tests/test_salcs.py:33:18: E128 continuation line under-indented for visual indent
chempy/symmetry/tests/test_salcs.py:40:18: E128 continuation line under-indented for visual indent
chempy/symmetry/tests/test_salcs.py:42:18: E128 continuation line under-indented for visual indent
chempy/symmetry/tests/test_salcs.py:44:11: E275 missing whitespace after keyword
chempy/symmetry/tests/test_salcs.py:48:17: E222 multiple spaces after operator
chempy/symmetry/tests/test_salcs.py:51:11: E275 missing whitespace after keyword
chempy/symmetry/tests/test_salcs.py:63:11: E275 missing whitespace after keyword
chempy/symmetry/tests/test_salcs.py:64:11: E275 missing whitespace after keyword

I'll let you decide if you want to correct any of those (perhaps the unused imports at least?) or add some more of those "error codes" to to ignore list.

Fixed the formatting issues, including the unused imports, either by deleting them or adding additional tests to test_salcs.py to use those imports. Added additional calc_salcs_projection() tests for a wider range of point group coverage. Removed xfail - this was used in the development and is no longer really needed.
The docstrings were also updated and improved with more details and examples. Represented the floats in atom_contribution in tables.py as sympy objects to be more exact.
@weisscharlesj
Copy link
Contributor Author

I fixed all of the above formatting errors. I also added some references, converted a few missed floats to sympy representations, and added a few more tests for better code and point group coverage. Let me know if there is anything else you'd like changed. Thanks!

@bjodah bjodah merged commit 5480317 into bjodah:master Sep 5, 2025
1 of 2 checks passed
@bjodah
Copy link
Owner

bjodah commented Sep 5, 2025

Thank you @weisscharlesj for this PR! I'll try to make a new release with these features as soon as possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants