|
12 | 12 |
|
13 | 13 | from . import _plot as plt |
14 | 14 | from . import _array as _a |
15 | | -from ._math_small import is_ascending |
| 15 | +from ._math_small import is_ascending, cross3 |
16 | 16 | from ._indices import indices_in_sphere_with_dist, indices_le, indices_gt_le |
17 | 17 | from ._indices import list_index_le |
18 | 18 | from .messages import info, warn, SislError |
@@ -2450,31 +2450,57 @@ def reverse(self, atom=None): |
2450 | 2450 | xyz[atom, :] = self.xyz[atom[::-1], :] |
2451 | 2451 | return self.__class__(xyz, atom=self.atoms.reverse(atom), sc=self.sc.copy()) |
2452 | 2452 |
|
2453 | | - def mirror(self, plane, atom=None): |
2454 | | - """ Mirrors the atomic coordinates by multiplying by -1 |
| 2453 | + def mirror(self, method, atom=None): |
| 2454 | + r""" Mirrors the atomic coordinates about the plane |
2455 | 2455 |
|
2456 | 2456 | This will typically move the atomic coordinates outside of the unit-cell. |
2457 | 2457 | This method should be used with care. |
2458 | 2458 |
|
2459 | 2459 | Parameters |
2460 | 2460 | ---------- |
2461 | | - plane : {'xy'/'ab', 'yz'/'bc', 'xz'/'ac'} |
2462 | | - mirror the structure across the lattice vector plane |
| 2461 | + method : {'xy'/'z', ..., 'ab', ..., v} |
| 2462 | + mirror the structure about a Cartesian direction (``x``, ``y``, ``z``), |
| 2463 | + plane (``xy``, ``xz``, ``yz``) or about user defined vectors (``v``). |
| 2464 | + A vector may also be specified by ``'ab'`` which is the vector normal |
| 2465 | + to the plane spanned by the first and second lattice vector. |
| 2466 | + or user defined vector (`v`) |
2463 | 2467 | atom : array_like, optional |
2464 | 2468 | only mirror a subset of atoms |
2465 | 2469 | """ |
2466 | | - if not atom is None: |
2467 | | - atom = self._sanitize_atom(atom) |
2468 | | - else: |
| 2470 | + if atom is None: |
2469 | 2471 | atom = slice(None) |
| 2472 | + else: |
| 2473 | + atom = self._sanitize_atom(atom) |
| 2474 | + |
2470 | 2475 | g = self.copy() |
2471 | | - lplane = ''.join(sorted(plane.lower())) |
2472 | | - if lplane in ['xy', 'ab']: |
2473 | | - g.xyz[atom, 2] *= -1 |
2474 | | - elif lplane in ['yz', 'bc']: |
2475 | | - g.xyz[atom, 0] *= -1 |
2476 | | - elif lplane in ['xz', 'ac']: |
2477 | | - g.xyz[atom, 1] *= -1 |
| 2476 | + if isinstance(method, str): |
| 2477 | + method = ''.join(sorted(method.lower())) |
| 2478 | + if method in ['z', 'xy']: |
| 2479 | + g.xyz[atom, 2] *= -1 |
| 2480 | + elif method in ['x', 'yz']: |
| 2481 | + g.xyz[atom, 0] *= -1 |
| 2482 | + elif method in ['y', 'xz']: |
| 2483 | + g.xyz[atom, 1] *= -1 |
| 2484 | + elif method == 'ab': |
| 2485 | + method = cross3(self.cell[0], self.cell[1]) |
| 2486 | + elif method == 'ac': |
| 2487 | + method = cross3(self.cell[0], self.cell[2]) |
| 2488 | + elif method == 'bc': |
| 2489 | + method = cross3(self.cell[1], self.cell[2]) |
| 2490 | + |
| 2491 | + if not isinstance(method, str): |
| 2492 | + # it has to be an array of length 3 |
| 2493 | + # Mirror about a user defined vector |
| 2494 | + method = _a.asarrayd(method).copy() |
| 2495 | + method /= fnorm(method) |
| 2496 | + |
| 2497 | + # project onto vector |
| 2498 | + vp = self.xyz[atom, :].dot(method) * 2 |
| 2499 | + |
| 2500 | + # convert coordinates |
| 2501 | + # first subtract the projection, then its mirror position |
| 2502 | + self.xyz[atom, :] -= vp.reshape(-1, 1) * method.reshape(1, 3) |
| 2503 | + |
2478 | 2504 | return self.__class__(g.xyz, atom=g.atom, sc=self.sc.copy()) |
2479 | 2505 |
|
2480 | 2506 | @property |
|
0 commit comments