|
52 | 52 | from deepmd.dpmodel.utils.seed import ( |
53 | 53 | child_seed, |
54 | 54 | ) |
| 55 | +from deepmd.utils.charge_state import ( |
| 56 | + CHARGE_OFFSET, |
| 57 | + CHARGE_TABLE_ROWS, |
| 58 | + MULTIPLICITY_TABLE_ROWS, |
| 59 | +) |
55 | 60 | from deepmd.utils.version import ( |
56 | 61 | check_version_compatibility, |
57 | 62 | ) |
|
69 | 74 | Array, |
70 | 75 | ) |
71 | 76 |
|
72 | | -#: Rows of the charge table, covering integer charges in ``[-100, 99]``. |
73 | | -CHARGE_TABLE_ROWS = 200 |
74 | | - |
75 | | -#: Index of the neutral charge row, so row ``CHARGE_OFFSET + Q`` holds ``Q``. |
76 | | -CHARGE_OFFSET = 100 |
77 | | - |
78 | | -#: Rows of the spin table, covering integer multiplicities below this bound. |
79 | | -MULTIPLICITY_TABLE_ROWS = 100 |
80 | | - |
81 | | -#: Half-open range of representable total charges, in units of the elementary |
82 | | -#: charge. |
83 | | -CHARGE_RANGE = (-CHARGE_OFFSET, CHARGE_TABLE_ROWS - CHARGE_OFFSET) |
84 | | - |
85 | | -#: Half-open range of representable spin multiplicities. |
86 | | -MULTIPLICITY_RANGE = (0, MULTIPLICITY_TABLE_ROWS) |
87 | | - |
88 | | -#: Name of each value of a charge state, in order, for diagnostics. |
89 | | -CHARGE_STATE_FIELDS = ("charge", "multiplicity") |
90 | | - |
91 | | -#: Half-open row range addressed by each value of a charge state, in order. |
92 | | -#: A condition is a pair of table row indices, so a host-side boundary that |
93 | | -#: knows these ranges can reject an unaddressable state without knowing which |
94 | | -#: descriptor holds the tables. |
95 | | -CHARGE_STATE_TABLE_RANGES = (CHARGE_RANGE, MULTIPLICITY_RANGE) |
96 | | - |
97 | | - |
98 | | -def validate_charge_state(charge_spin: Any) -> list[float]: |
99 | | - """Check that a frame condition addresses a row of each embedding table. |
100 | | -
|
101 | | - Both tables are indexed directly by the condition, and neither the gather |
102 | | - nor the compiled kernel bounds-checks that index, so an out-of-range value |
103 | | - would read past the table. Every host-side boundary that accepts a charge |
104 | | - state therefore passes it through here first. The per-forward path is |
105 | | - deliberately not guarded: its values come from the data pipeline, which |
106 | | - owns their validity exactly as it owns the validity of an atom type. |
107 | | -
|
108 | | - Parameters |
109 | | - ---------- |
110 | | - charge_spin |
111 | | - A pair ``[charge, multiplicity]``, in any sequence form. |
112 | | -
|
113 | | - Returns |
114 | | - ------- |
115 | | - list[float] |
116 | | - The same pair, as two floats. |
117 | | -
|
118 | | - Raises |
119 | | - ------ |
120 | | - ValueError |
121 | | - If the pair does not hold exactly two integral values within the |
122 | | - representable ranges. |
123 | | - """ |
124 | | - values = [float(value) for value in np.reshape(np.asarray(charge_spin), (-1,))] |
125 | | - if len(values) != 2: |
126 | | - raise ValueError( |
127 | | - f"A charge state must be a `[charge, multiplicity]` pair, got " |
128 | | - f"{len(values)} values" |
129 | | - ) |
130 | | - for value, name, (low, high) in zip( |
131 | | - values, |
132 | | - CHARGE_STATE_FIELDS, |
133 | | - CHARGE_STATE_TABLE_RANGES, |
134 | | - strict=True, |
135 | | - ): |
136 | | - if not np.isfinite(value) or value != int(value): |
137 | | - raise ValueError(f"The {name} must be an integer, got {value}") |
138 | | - if not low <= value < high: |
139 | | - raise ValueError( |
140 | | - f"The {name} must lie in [{low}, {high}), got {int(value)}" |
141 | | - ) |
142 | | - return values |
143 | | - |
144 | 77 |
|
145 | 78 | class ChargeStateEmbedding(NativeOP): |
146 | 79 | r"""Embed the frame charge and spin multiplicity into two condition vectors. |
|
0 commit comments