Skip to content

Commit af3a0ad

Browse files
authored
Merge pull request #240 from weisscharlesj/symmetry_v2
Symmetry v2 - minor fixes and enhancements
2 parents 209135c + bda88ad commit af3a0ad

File tree

6 files changed

+679
-61
lines changed

6 files changed

+679
-61
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,6 @@ sundials-*.tar.gz
3030
tmp/
3131
.coverage
3232
htmlcov/
33+
.DS_Store
34+
*.ini
35+
*.ini.bak

chempy/symmetry/representations.py

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"""
1010

1111
import numpy as np
12+
from functools import wraps
1213
from tabulate import tabulate
1314

1415
# data tables in character_tables.py file
@@ -21,7 +22,8 @@
2122
atom_contribution,
2223
mulliken,
2324
headers,
24-
row_coeffs
25+
row_coeffs,
26+
column_coeffs
2527
)
2628

2729

@@ -39,7 +41,17 @@ def print_header(group):
3941
-------
4042
Prints character table header indicating order of symmetry operations.
4143
"""
42-
print(*headers[group.lower()])
44+
symbols = headers[group.lower()]
45+
numbers = column_coeffs[group.lower()]
46+
47+
header = []
48+
for i in range(len(numbers)):
49+
if numbers[i] != 1:
50+
header.append(str(numbers[i]) + symbols[i])
51+
else:
52+
header.append(symbols[i])
53+
54+
print(*header)
4355

4456

4557
def print_point_groups():
@@ -98,8 +110,17 @@ def print_table(group):
98110
99111
"""
100112
group = group.lower()
101-
columns = headers[group]
113+
symbols = headers[group]
102114
mull_symbols = mulliken[group]
115+
numbers = column_coeffs[group.lower()]
116+
117+
# generate list of symmetry symbols with coefficients
118+
header = []
119+
for i in range(len(numbers)):
120+
if numbers[i] != 1:
121+
header.append(str(numbers[i]) + symbols[i])
122+
else:
123+
header.append(symbols[i])
103124

104125
# generate list of Mulliken symbols including duplicates if imaginary rep
105126
if group.lower() in row_coeffs.keys():
@@ -109,9 +130,12 @@ def print_table(group):
109130
else:
110131
rows = mull_symbols
111132

112-
table = [[group.capitalize(), *columns]]
113-
for i_row in range(len(rows)):
114-
table.append([rows[i_row], *tables[group][i_row]])
133+
table = [[group.capitalize(), *header]]
134+
if group == 'c1':
135+
table.append([rows, 1])
136+
else:
137+
for i_row in range(len(rows)):
138+
table.append([rows[i_row], *tables[group][i_row]])
115139

116140
print(tabulate(table, tablefmt='rounded_grid'))
117141

@@ -208,6 +232,7 @@ def return_dict_method(func):
208232
Dictionary.
209233
210234
"""
235+
@wraps(func)
211236
def wrapper(self, *args, **kwargs):
212237
if kwargs.get('to_dict'):
213238
keys = mulliken[self.group.lower()]
@@ -245,12 +270,12 @@ def decomp(self, to_dict=False):
245270
--------
246271
>>> rep = Reducible([9, -1, 3, 1], 'c2v', all_motion=True)
247272
>>> rep.decomp()
248-
>>> array([3, 1, 3, 2])
273+
array([3, 1, 3, 2])
249274
>>> rep = Reducible([15, 0, 0, 7, -2, -2], 'C3h', all_motion=False)
250275
>>> rep.decomp()
251-
>>> array([3, 4, 2, 1])
276+
array([3, 4, 2, 1])
252277
>>> rep.decomp(to_dict=True)
253-
>>> {"A'": 3, 'A"': 4, "E'": 2, 'E"': 1}
278+
{"A'": 3, "E'": 4, 'A"': 2, 'E"': 1}
254279
"""
255280
table = sympy_to_num(tables[self.group])
256281
gamma = np.array(self.gamma)
@@ -286,8 +311,8 @@ def vibe_modes(self, to_dict=False):
286311
>>> rep = Reducible([9, -1, 3, 1], 'c2v', all_motion=True)
287312
>>> rep.vibe_modes()
288313
array([2, 0, 1, 0])
289-
>>> rep.vibe_modes(to_dict)
290-
>>> {'A1': 2, 'A2': 0, 'B1': 1, 'B2': 0}
314+
>>> rep.vibe_modes(to_dict=True)
315+
{'A1': 2, 'A2': 0, 'B1': 1, 'B2': 0}
291316
"""
292317
if self.all_motion is False:
293318
return self.decomp()
@@ -320,12 +345,12 @@ def ir_active(self, to_dict=False):
320345
--------
321346
>>> rep = Reducible([9, -1, 3, 1], 'c2v', all_motion=True)
322347
>>> rep.ir_active()
323-
>>> array([2, 0, 1, 0])
348+
array([2, 0, 1, 0])
324349
>>> rep = Reducible([5, 2, 1, 3, 0, 3], 'd3h', all_motion=False)
325350
>>> rep.ir_active()
326-
>>> array([0, 0, 1, 0, 1, 0])
351+
array([0, 0, 1, 0, 1, 0])
327352
>>> rep.ir_active(to_dict=True)
328-
>>> {"A1'": 0, "A2'": 0, "E'": 1, 'A1"': 0, 'A2"': 1, 'E"': 0}
353+
{"A'1": 0, "A'2": 0, "E'": 1, 'A"1': 0, 'A"2': 1, 'E"': 0}
329354
330355
"""
331356
return self.vibe_modes() * np.array(IR_active[self.group])
@@ -353,12 +378,12 @@ def raman_active(self, to_dict=False):
353378
--------
354379
>>> rep = Reducible([9, -1, 3, 1], 'c2v', all_motion=True)
355380
>>> rep.raman_active([3, 1, 3, 2, 'C2v'])
356-
>>> array([2, 0, 1, 0])
381+
array([2, 0, 1, 0])
357382
>>> rep = Reducible([5, 2, 1, 3, 0, 3], 'd3h', all_motion=False)
358383
>>> rep.raman_active()
359-
>>> array([2, 0, 1, 0, 0, 0])
384+
array([2, 0, 1, 0, 0, 0])
360385
>>> rep.raman_active(to_dict=True)
361-
>>> {"A1'": 2, "A2'": 0, "E'": 1, 'A1"': 0, 'A2"': 0, 'E"': 0}
386+
{"A'1": 2, "A'2": 0, "E'": 1, 'A"1': 0, 'A"2': 0, 'E"': 0}
362387
363388
"""
364389
return self.vibe_modes() * np.array(Raman_active[self.group])

chempy/symmetry/salcs.py

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from math import cos, sin, radians, isclose
1717
import numpy as np
18+
from functools import wraps
1819
import sympy
1920
sympy.init_printing(pretty_print=False)
2021

@@ -36,9 +37,9 @@ def return_dict(func):
3637
Dictionary.
3738
3839
"""
40+
@wraps(func)
3941
def wrapper(*args, **kwargs):
4042
if kwargs.get('to_dict'):
41-
print(args)
4243
keys = mulliken[args[1].lower()]
4344
values = func(*args, **kwargs)
4445
return dict(zip(keys, values))
@@ -82,6 +83,35 @@ def _expand_irreducible(irred, group):
8283
return expanded_irred
8384

8485

86+
def _normalize_salcs_expr(salcs):
87+
"""
88+
Normalize SALC composed of sympy expressions.
89+
90+
Normalizes SALC by dividing each SALC through by the largest value in
91+
that SALC.
92+
93+
Parameters
94+
----------
95+
salcs : List or nested list of sympy expressions.
96+
Nested list of SALCs.
97+
98+
Returns
99+
-------
100+
np.array
101+
102+
"""
103+
normalized_values = []
104+
for salc in salcs:
105+
if isinstance(salc, list):
106+
normalized_values.append(_normalize_salcs_expr(salc))
107+
elif salc == 0:
108+
normalized_values.append(salc)
109+
else:
110+
normalized_values.append(salc.as_poly().monic().as_expr())
111+
112+
return normalized_values
113+
114+
85115
@return_dict
86116
def calc_salcs_projection(projection, group, to_dict=False):
87117
"""
@@ -115,9 +145,9 @@ def calc_salcs_projection(projection, group, to_dict=False):
115145
>>> import sympy
116146
>>> a, b, c = sympy.symbols('a b c')
117147
>>> calc_salcs_projection([a, b, c, a, b, c], 'c3v')
118-
>>> [2*a + 2*b + 2*c, , 0, 2*a - b - c]
148+
[a + b + c, 0, a - b/2 - c/2]
119149
>>> calc_salcs_projection([a, b, c, a, b, c], 'c3v', to_dict=True)
120-
>>> {'A1': 2*a + 2*b + 2*c, 'A2': 0, 'E': 2*a - b - c}
150+
{'A1': a + b + c, 'A2': 0, 'E': a - b/2 - c/2}
121151
122152
"""
123153
salcs = []
@@ -127,7 +157,7 @@ def calc_salcs_projection(projection, group, to_dict=False):
127157
np.array(projection))
128158
salcs.append(np.sum(product))
129159

130-
return salcs
160+
return _normalize_salcs_expr(salcs)
131161

132162

133163
# USING SYMMETRY FUNCTIONS
@@ -242,10 +272,13 @@ def _normalize_salcs(salcs):
242272
for value in salcs:
243273
if isinstance(value, list):
244274
normalized_values.append(_normalize_salcs(value))
245-
elif isinstance(value, int):
275+
elif value == 0:
246276
normalized_values.append(value)
247277
else:
248-
normalized_values.append(round(value / max(salcs, key=abs), 2))
278+
coeff = round(value / max(salcs, key=abs), 2)
279+
if coeff % 1 < 0.01:
280+
coeff = sympy.Integer(coeff)
281+
normalized_values.append(coeff)
249282

250283
return normalized_values
251284

@@ -312,11 +345,11 @@ def calc_salcs_func(ligands, group, symbols, mode='vector', to_dict=False):
312345
ligand : list or nested list
313346
Nested list of ligand positions as xyz coordinates (mode='vector')
314347
or angles (mode='angle').
315-
symbols : SymPy symbols
316-
SymPy symbols representing outer ligands or atoms.
317348
group : str
318349
Point group Schoenflies notation (e.g., 'C2v'). This is
319350
case-insensitive.
351+
symbols : SymPy symbols
352+
SymPy symbols representing outer ligands or atoms.
320353
mode : 'vector' or 'angle'
321354
Whether the position of ligands or outer atoms are provided in xyz
322355
coordinates ('vector') or [theta, phi] angles ('angle').
@@ -332,17 +365,13 @@ def calc_salcs_func(ligands, group, symbols, mode='vector', to_dict=False):
332365
--------
333366
>>> import sympy
334367
>>> a, b, c, d = sympy.symbols('a b c d')
335-
# for a square planar complex
336-
>>> calc_salcs_func([[1, 0, 0], [0, 1, 0], [-1, 0, 0], [0, -1, 0]],
337-
'd4h', [a, b, c, d], mode='vector')
338-
>>> [a + b + c + d, 0, a - b + c - d, 0, 0, 0, 0, 0, 0, [a - c, b - d]]
339-
# for a trigonal planar complex
340-
>>> calc_salcs_func([[0, -90], [120, -90], [240, -90]], 'd3h', [a, b, c],
341-
mode='angle')
342-
>>> [1.0*a + 1.0*b + 1.0*c, 0,
343-
[1.0*a - 0.5*b - 0.5*c,
344-
1.0*b - 1.0*c, 1.0*a - 0.5*b - 0.5*c,
345-
1.0*b - 1.0*c], 0, 0, 0]
368+
>>> coords = [[1, 0, 0], [0, 1, 0], [-1, 0, 0], [0, -1, 0]]
369+
>>> calc_salcs_func(coords, 'd4h', [a, b, c, d], mode='vector')
370+
[a + b + c + d, 0, a - b + c - d, 0, 0, 0, 0, 0, 0, [a - c, b - d]]
371+
>>> coords = [[0, -90], [120, -90], [240, -90]]
372+
>>> calc_salcs_func(coords, 'd3h', [a, b, c], mode='angle')
373+
[a + b + c, 0, [a - 0.5*b - 0.5*c, b - c, a - 0.5*b - 0.5*c, b - c], \
374+
0, 0, 0]
346375
347376
References
348377
----------

chempy/symmetry/tables.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,11 @@
237237
[1, i,-1,-i],
238238
[1,-i,-1, i]]),
239239
's6': np.array([[1, 1, 1, 1, 1, 1],
240-
[1, E6, E6_, 1, E6, E6_],
241-
[1, E6_, E6, 1, E6_, E6],
240+
[1, E3, E3_, 1, E3, E3_],
241+
[1, E3_, E3, 1, E3_, E3],
242242
[1, 1, 1,-1,-1,-1],
243-
[1, E6, E6_,-1,-E6,-E6_],
244-
[1, E6_, E6,-1,-E6_,-E6]]),
243+
[1, E3, E3_,-1,-E3,-E3_],
244+
[1, E3_, E3,-1,-E3_,-E3]]),
245245
's8': np.array([[1, 1, 1, 1, 1, 1, 1, 1],
246246
[1,-1, 1,-1, 1,-1, 1,-1],
247247
[1, E8, i, -E8_, -1, -E8, -i, E8_],
@@ -368,6 +368,7 @@
368368
}
369369

370370
# symmetry operation column headers for print functions
371+
# Note: D8h has S^3_8 before S_8. See J. Chem. Educ. 84(11), 1882-1884.
371372
headers = {'c1': ('E'),
372373
'cs': ('E', '\u03C3h'),
373374
'ci': ('E', 'i'),
@@ -398,7 +399,7 @@
398399
'd4h': ('E', 'C\u2084', 'C\u2082', 'C\u2082'+'\u2032', 'C\u2082'+'\u2033', 'i', 'S\u2084', '\u03C3h', '\u03C3v', '\u03C3d'),
399400
'd5h': ('E', 'C\u2085', 'C\u2085'+'\u00B2', 'C\u2082', '\u03C3h', 'S\u2085', 'S\u2085'+'\u00B3', '\u03C3v'),
400401
'd6h': ('E', 'C\u2086', 'C\u2083', 'C\u2082', 'C\u2082'+'\u2032', 'C\u2082'+'\u2033', 'i', 'S\u2083', 'S\u2086', '\u03C3h', '\u03C3d', '\u03C3v'),
401-
'd8h': ('E', 'C\u2088', 'C\u2088'+'\u00B3', 'C\u2082', 'C\u2082'+'\u2032', 'C\u2082'+'\u2033', 'i', 'S\u2088', 'S\u2088'+'\u00B3', 'S\u2084', '\u03C3h', '\u03C3d', '\u03C3v'),
402+
'd8h': ('E', 'C\u2088', 'C\u2088'+'\u00B3', 'C\u2084', 'C\u2082', 'C\u2082'+'\u2032', 'C\u2082'+'\u2033', 'i', 'S\u2088'+'\u00B3', 'S\u2088', 'S\u2084', '\u03C3h', '\u03C3d', '\u03C3v'),
402403
'd2d': ('E', 'S\u2084', 'C\u2082', 'C\u2082'+'\u2032', '\u03C3d'),
403404
'd3d': ('E', 'C\u2083', 'C\u2082', 'i', 'S\u2086', '\u03C3d'),
404405
'd4d': ('E', 'S\u2088', 'C\u2084', 'S\u2088'+'\u00B3', 'C\u2082', 'C\u2082'+'\u2032', '\u03C3d'),
@@ -468,6 +469,7 @@
468469

469470
# number of rotational plus translation modes in each irreducible representations
470471
# used to subtract from all motions to obtain just vibrational modes
472+
# Note: S8 has (Rx, Ry) in E3. See J. Chem. Educ. 84(11), 1882-1884.
471473
rot_trans_modes = {'c1': (0,),
472474
'cs': (2, 2),
473475
'ci': (2, 2),
@@ -506,7 +508,7 @@
506508
'd6d': (0, 1, 0, 1, 1, 0, 0, 0, 1),
507509
's4': (1, 1, 2),
508510
's6': (1, 1, 1, 1),
509-
's8': (1, 1, 2, 0, 0),
511+
's8': (1, 1, 1, 0, 1),
510512
't': (0, 0, 2),
511513
'th': (0, 0, 0, 0, 1, 1),
512514
'td': (0, 0, 0, 1, 1),

0 commit comments

Comments
 (0)