Coverage for calorine/nep/tensor_conventions.py: 100%
13 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-23 12:50 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-23 12:50 +0000
1import numpy as np
3# ASE's own canonical Voigt-6 convention (see ase.stress.voigt_notation).
4ASE_VOIGT6_ORDER = ('xx', 'yy', 'zz', 'yz', 'xz', 'xy')
6# Reduced-6 order used by the standalone `nep` executable's virial_*.out,
7# stress_*.out, and polarizability_*.out files (training/prediction mode),
8# and therefore by calorine.nep.io.read_structures. Established by GPUMD's
9# main_nep/structure.cu: `reduced_index = {0, 3, 5, 3, 1, 4, 5, 4, 2}` (maps
10# full-3x3 flat positions onto this 6-slot array; used identically for
11# parsing both `virial=` and `pol=` extxyz fields). Also matches
12# `get_polarizability` in src/nepy/nep.cpp. Independently confirmed
13# empirically for stress by applying isolated shear strains and checking
14# that each produces a response concentrated at the matching component.
15NEP_REDUCED6_ORDER = ('xx', 'yy', 'zz', 'xy', 'yz', 'xz')
17# Raw, unreduced, row-major full-3x3 order for Born effective charges (BEC
18# is not symmetric, so no reduced form applies), used identically by
19# src/nepy/nep.cpp (g_bec[n1+N*0..8]), GPUMD's src/force/nep_charge.cu
20# (identical kernel/layout), and src/measure/dump_xyz.cu (writes `bec` with
21# no reindexing).
22BEC_FULL9_ORDER = ('xx', 'xy', 'xz', 'yx', 'yy', 'yz', 'zx', 'zy', 'zz')
25def reduced6_to_full_3x3(values, order=NEP_REDUCED6_ORDER):
26 """Expands an array whose last axis holds a reduced-6 symmetric tensor
27 (in the component order given by `order`) into an array with two
28 trailing 3x3 axes, e.g. shape (6,) -> (3,3) or (natoms,3,6) -> (natoms,3,3,3).
29 Use ``order=ASE_VOIGT6_ORDER`` for ASE-Voigt input (equivalent to
30 :func:`ase.stress.voigt_6_to_full_3x3_stress`, provided here for
31 uniformity with the other conventions in this module).
33 Parameters
34 ----------
35 values
36 Array whose last axis has length 6, holding a reduced symmetric
37 tensor in the component order given by `order`.
38 order
39 Component order of `values`' last axis. Defaults to
40 :data:`NEP_REDUCED6_ORDER`.
41 """
42 values = np.asarray(values)
43 idx = {label: i for i, label in enumerate(order)}
44 index_grid = np.array([
45 [idx['xx'], idx['xy'], idx['xz']],
46 [idx['xy'], idx['yy'], idx['yz']],
47 [idx['xz'], idx['yz'], idx['zz']],
48 ])
49 return values[..., index_grid]
52def reduced6_permutation(source_order, target_order=ASE_VOIGT6_ORDER):
53 """Returns the index array that permutes a reduced-6 array from
54 `source_order` to `target_order`.
56 Parameters
57 ----------
58 source_order
59 Component order to permute from.
60 target_order
61 Component order to permute to. Defaults to :data:`ASE_VOIGT6_ORDER`.
62 """
63 return [source_order.index(label) for label in target_order]
66def nep_reduced6_to_ase_voigt6(values):
67 """Permutes a reduced-6 symmetric-tensor array (last axis) from `nep`'s
68 native order (:data:`NEP_REDUCED6_ORDER`) to ASE's Voigt-6 order
69 (:data:`ASE_VOIGT6_ORDER`).
71 Parameters
72 ----------
73 values
74 Array whose last axis has length 6, holding a reduced symmetric
75 tensor in :data:`NEP_REDUCED6_ORDER`.
76 """
77 return np.asarray(values)[..., reduced6_permutation(NEP_REDUCED6_ORDER)]