(git:691081d)
Loading...
Searching...
No Matches
topology_snapshot.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! **************************************************************************************************
9!> \brief Reader and physical moving-basis links for version-1 topology snapshots.
10! **************************************************************************************************
12 USE ai_moments, ONLY: cossin
13 USE cp_files, ONLY: close_file,&
15 USE ieee_arithmetic, ONLY: ieee_is_finite
16 USE kinds, ONLY: dp
17 USE orbital_pointers, ONLY: indco,&
19#include "./base/base_uses.f90"
20
21 IMPLICIT NONE
22 PRIVATE
23 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'topology_snapshot'
24 REAL(KIND=dp), PARAMETER :: pi = 3.1415926535897932384626433832795_dp, cell_tol = 1.e-10_dp
25 TYPE :: snapshot_shell_type
26 INTEGER :: first = 0, count = 0, lmin = 0, lmax = 0
27 REAL(KIND=dp) :: center(3) = 0.0_dp, radius = 0.0_dp
28 REAL(KIND=dp), ALLOCATABLE :: exponent(:), radii(:), contraction(:, :)
29 END TYPE snapshot_shell_type
30 TYPE :: snapshot_atom_type
31 INTEGER :: kind = 0, count = 0
32 REAL(KIND=dp) :: center(3) = 0.0_dp
33 TYPE(snapshot_shell_type), ALLOCATABLE :: shells(:)
34 END TYPE snapshot_atom_type
36 INTEGER :: nao = 0, rank = 0, nspin = 0, channel = 0
37 INTEGER :: periodic(3) = 0
38 REAL(kind=dp) :: cell(3, 3) = 0.0_dp, inverse(3, 3) = 0.0_dp, k(3) = 0.0_dp
39 TYPE(snapshot_atom_type), ALLOCATABLE :: atoms(:)
40 INTEGER, ALLOCATABLE :: bands(:)
41 REAL(kind=dp), ALLOCATABLE :: energies(:)
42 COMPLEX(KIND=dp), ALLOCATABLE :: coefficients(:, :)
43 END TYPE snapshot_type
45CONTAINS
46
47! **************************************************************************************************
48!> \brief Read a single point, retaining only its states; reject malformed/truncated exports.
49!> \param filename Version-1 STATE_EXPORT file
50!> \param point One-based point index
51!> \param state Selected frame
52! **************************************************************************************************
53 SUBROUTINE read_snapshot(filename, point, state)
54 CHARACTER(LEN=*), INTENT(IN) :: filename
55 INTEGER, INTENT(IN) :: point
56 TYPE(snapshot_type), INTENT(OUT) :: state
57
58 CHARACTER(LEN=256) :: header
59 INTEGER :: first, i, ia, ic, ios, ip, iset, ix, j, &
60 nat, nc, nk, np, ns, nt, offset, p, &
61 powers(3), refpowers(3), unit
62 LOGICAL, ALLOCATABLE :: covered(:)
63 REAL(kind=dp) :: det, k(3), pair(2)
64 REAL(kind=dp), ALLOCATABLE :: energies(:), row(:)
65
66 CALL open_file(filename, unit_number=unit, file_status='OLD', file_action='READ')
67 READ (unit, '(A)', iostat=ios) header
68 cpassert(ios == 0)
69 IF (trim(header) /= 'CP2K_TOPOLOGY_STATE 1') THEN
70 cpabort('Unsupported topology snapshot version')
71 END IF
72 READ (unit, *, iostat=ios) nat, state%nao, state%rank, nk, state%nspin, nt, state%channel
73 cpassert(ios == 0)
74 IF (min(nat, state%nao, state%rank, nk, nt, state%channel) < 1) THEN
75 cpabort('Invalid snapshot dimensions')
76 END IF
77 IF (state%rank > nt .OR. point < 1 .OR. point > nk) THEN
78 cpabort('Invalid snapshot point or rank')
79 END IF
80 IF (state%nspin /= 1 .AND. state%nspin /= 2) THEN
81 cpabort('Invalid snapshot spin components')
82 END IF
83 ALLOCATE (state%bands(state%rank), state%energies(nt), energies(nt), &
84 state%coefficients(state%nao*state%nspin, state%rank), state%atoms(nat))
85 READ (unit, *, iostat=ios) state%bands
86 cpassert(ios == 0)
87 IF (any(state%bands < 1) .OR. any(state%bands > nt)) THEN
88 cpabort('Invalid snapshot band indices')
89 END IF
90 DO i = 2, state%rank
91 IF (state%bands(i) <= state%bands(i - 1)) THEN
92 cpabort('Snapshot bands must be strictly ordered')
93 END IF
94 END DO
95 DO j = 1, 3
96 READ (unit, *, iostat=ios) state%cell(:, j)
97 cpassert(ios == 0)
98 END DO
99 IF (.NOT. all(ieee_is_finite(state%cell))) THEN
100 cpabort('Nonfinite snapshot cell')
101 END IF
102 associate(a => state%cell, b => state%inverse)
103 b(1, :) = [a(2, 2)*a(3, 3) - a(2, 3)*a(3, 2), a(1, 3)*a(3, 2) - a(1, 2)*a(3, 3), a(1, 2)*a(2, 3) - a(1, 3)*a(2, 2)]
104 b(2, :) = [a(2, 3)*a(3, 1) - a(2, 1)*a(3, 3), a(1, 1)*a(3, 3) - a(1, 3)*a(3, 1), a(1, 3)*a(2, 1) - a(1, 1)*a(2, 3)]
105 b(3, :) = [a(2, 1)*a(3, 2) - a(2, 2)*a(3, 1), a(1, 2)*a(3, 1) - a(1, 1)*a(3, 2), a(1, 1)*a(2, 2) - a(1, 2)*a(2, 1)]
106 det = dot_product(a(:, 1), b(1, :))
107 IF (abs(det) < cell_tol) THEN
108 cpabort('Singular snapshot cell')
109 END IF
110 b(:, :) = b/det
111 END associate
112 READ (unit, *, iostat=ios) state%periodic
113 cpassert(ios == 0)
114 IF (any(state%periodic < 0) .OR. any(state%periodic > 1)) THEN
115 cpabort('Invalid snapshot periodicity')
116 END IF
117 offset = 0
118 DO ia = 1, nat
119 associate(atom => state%atoms(ia))
120 READ (unit, *, iostat=ios) ix, atom%kind, ns, atom%count
121 cpassert(ios == 0)
122 IF (ix /= ia .OR. min(atom%kind, ns, atom%count) < 1) THEN
123 cpabort('Invalid snapshot atom')
124 END IF
125 READ (unit, *, iostat=ios) atom%center
126 cpassert(ios == 0)
127 IF (.NOT. all(ieee_is_finite(atom%center))) THEN
128 cpabort('Nonfinite atom center')
129 END IF
130 ALLOCATE (atom%shells(ns), covered(atom%count))
131 covered(:) = .false.
132 DO iset = 1, ns
133 associate(s => atom%shells(iset))
134 READ (unit, *, iostat=ios) first, s%count, np, nc, s%lmin, s%radius
135 cpassert(ios == 0)
136 IF (min(first, s%count, np, nc) < 1 .OR. s%lmin < 0) THEN
137 cpabort('Invalid Gaussian shell')
138 END IF
139 IF (.NOT. ieee_is_finite(s%radius) .OR. s%radius <= 0) THEN
140 cpabort('Invalid shell screening radius')
141 END IF
142 IF (first + s%count - 1 > atom%count) THEN
143 cpabort('Shell AO range exceeds atom')
144 END IF
145 IF (any(covered(first:first + s%count - 1))) THEN
146 cpabort('Overlapping shell AO ranges')
147 END IF
148 covered(first:first + s%count - 1) = .true.
149 s%lmax = 0
150 DO WHILE ((s%lmax + 1)*(s%lmax + 2)*(s%lmax + 3)/6 < nc)
151 s%lmax = s%lmax + 1
152 END DO
153 IF ((s%lmax + 1)*(s%lmax + 2)*(s%lmax + 3)/6 /= nc .OR. s%lmin > s%lmax) THEN
154 cpabort('Invalid Cartesian Gaussian count')
155 END IF
156 CALL init_orbital_pointers(s%lmax + 1)
157 s%first = offset + first
158 s%center(:) = atom%center
159 ALLOCATE (s%exponent(np), s%radii(np), s%contraction(nc*np, s%count), row(s%count))
160 DO ip = 1, np
161 READ (unit, *, iostat=ios) s%exponent(ip), s%radii(ip)
162 cpassert(ios == 0)
163 IF (.NOT. ieee_is_finite(s%exponent(ip)) .OR. .NOT. ieee_is_finite(s%radii(ip))) THEN
164 cpabort('Nonfinite primitive Gaussian')
165 END IF
166 IF (s%exponent(ip) <= 0 .OR. s%radii(ip) <= 0) THEN
167 cpabort('Invalid primitive Gaussian')
168 END IF
169 DO ic = 1, nc
170 READ (unit, *, iostat=ios) powers, row
171 cpassert(ios == 0)
172 refpowers(:) = indco(:, ic)
173 IF (any(powers /= refpowers)) THEN
174 cpabort('Unexpected Cartesian Gaussian ordering')
175 END IF
176 IF (.NOT. all(ieee_is_finite(row))) THEN
177 cpabort('Nonfinite contraction')
178 END IF
179 s%contraction((ip - 1)*nc + ic, :) = row
180 END DO
181 END DO
182 DEALLOCATE (row)
183 END associate
184 END DO
185 IF (.NOT. all(covered)) THEN
186 cpabort('Incomplete atom AO coverage')
187 END IF
188 DEALLOCATE (covered)
189 offset = offset + atom%count
190 END associate
191 END DO
192 IF (offset /= state%nao) THEN
193 cpabort('Snapshot AO count mismatch')
194 END IF
195 DO p = 1, nk
196 READ (unit, *, iostat=ios) ix, k
197 cpassert(ios == 0)
198 IF (ix /= p .OR. .NOT. all(ieee_is_finite(k))) THEN
199 cpabort('Invalid snapshot k-point')
200 END IF
201 READ (unit, *, iostat=ios) energies
202 cpassert(ios == 0)
203 IF (.NOT. all(ieee_is_finite(energies))) THEN
204 cpabort('Nonfinite snapshot spectrum')
205 END IF
206 IF (any(energies(2:) < energies(:nt - 1))) THEN
207 cpabort('Unordered snapshot spectrum')
208 END IF
209 IF (p == point) THEN
210 state%k(:) = k
211 state%energies(:) = energies
212 END IF
213 DO j = 1, state%rank
214 DO i = 1, state%nao*state%nspin
215 READ (unit, *, iostat=ios) pair
216 cpassert(ios == 0)
217 IF (.NOT. all(ieee_is_finite(pair))) THEN
218 cpabort('Nonfinite state coefficient')
219 END IF
220 IF (p == point) state%coefficients(i, j) = cmplx(pair(1), pair(2), dp)
221 END DO
222 END DO
223 END DO
224 DO
225 READ (unit, '(A)', iostat=ios) header
226 IF (ios < 0) EXIT
227 IF (ios /= 0 .OR. len_trim(header) /= 0) THEN
228 cpabort('Unexpected trailing snapshot content')
229 END IF
230 END DO
231 CALL close_file(unit)
232 END SUBROUTINE read_snapshot
233
234! **************************************************************************************************
235!> \brief Apply screened Gaussian cross-geometry operator by atom blocks, without a dense AO matrix.
236!> \param a Left physical state
237!> \param b Right physical state
238!> \param ka Unwrapped left fractional k-point
239!> \param kb Unwrapped right fractional k-point
240!> \param overlap Selected-state overlap including all spinor components
241! **************************************************************************************************
242 SUBROUTINE snapshot_overlap(a, b, ka, kb, overlap)
243 TYPE(snapshot_type), INTENT(IN) :: a, b
244 REAL(kind=dp), INTENT(IN) :: ka(3), kb(3)
245 COMPLEX(KIND=dp), INTENT(OUT) :: overlap(:, :)
246
247 COMPLEX(KIND=dp) :: part(size(overlap, 1), size(overlap, 2))
248 INTEGER :: ia
249
250 IF (any(shape(overlap) /= [a%rank, b%rank])) THEN
251 cpabort('Incorrect overlap output dimensions')
252 END IF
253 IF (a%nspin /= b%nspin .OR. a%channel /= b%channel .OR. a%rank /= b%rank) THEN
254 cpabort('Changed spin or rank')
255 END IF
256 IF (any(a%bands /= b%bands)) THEN
257 cpabort('Changed selected band indices')
258 END IF
259 IF (SIZE(a%atoms) /= SIZE(b%atoms) .OR. a%nao /= b%nao) THEN
260 cpabort('Changed atom or AO count')
261 END IF
262 IF (maxval(abs(a%cell - b%cell)) > cell_tol .OR. any(a%periodic /= b%periodic)) THEN
263 cpabort('Changed cell')
264 END IF
265 IF (.NOT. all(ieee_is_finite(ka)) .OR. .NOT. all(ieee_is_finite(kb))) THEN
266 cpabort('Nonfinite k-point')
267 END IF
268 IF (maxval(abs(ka - a%k - anint(ka - a%k))) > cell_tol .OR. &
269 maxval(abs(kb - b%k - anint(kb - b%k))) > cell_tol) THEN
270 cpabort('Frame k-point does not match request')
271 END IF
272 overlap(:, :) = 0.0_dp
273!$OMP PARALLEL DO DEFAULT(NONE) SHARED(A,b,ka,kb) PRIVATE(ia,part) REDUCTION(+:overlap) SCHEDULE(DYNAMIC)
274 DO ia = 1, SIZE(a%atoms)
275 CALL atom_overlap(a, b, ia, ka, kb, part)
276 overlap(:, :) = overlap + part
277 END DO
278!$OMP END PARALLEL DO
279 END SUBROUTINE snapshot_overlap
280
281! **************************************************************************************************
282!> \brief Screened atom-row contribution using CP2K's cossin primitive integral recurrence.
283!> \param a Left physical state
284!> \param b Right physical state
285!> \param ia Left atom whose AO rows are contracted
286!> \param ka Unwrapped left fractional k-point
287!> \param kb Unwrapped right fractional k-point
288!> \param atom_link This atom's contribution to the selected-state overlap
289! **************************************************************************************************
290 SUBROUTINE atom_overlap(a, b, ia, ka, kb, atom_link)
291 TYPE(snapshot_type), INTENT(IN) :: a, b
292 INTEGER, INTENT(IN) :: ia
293 REAL(kind=dp), INTENT(IN) :: ka(3), kb(3)
294 COMPLEX(KIND=dp), INTENT(OUT) :: atom_link(:, :)
295
296 COMPLEX(KIND=dp) :: phase
297 COMPLEX(KIND=dp), ALLOCATABLE :: block(:, :), oc(:, :, :)
298 INTEGER :: af, bf, hi(3), i, ib, image(3), j, k, &
299 lo(3), nc_a, nc_b, s, sa, sb
300 REAL(kind=dp) :: bounds(3), cutoff, disp(3), q(3), rb(3)
301 REAL(kind=dp), ALLOCATABLE :: cosine(:, :), sine(:, :)
302
303 q(:) = 2.0_dp*pi*matmul(transpose(a%inverse), kb - ka)
304 atom_link(:, :) = 0.0_dp
305 DO sa = 1, SIZE(a%atoms(ia)%shells)
306 associate(left => a%atoms(ia)%shells(sa))
307 ALLOCATE (oc(left%count, b%rank, a%nspin))
308 oc(:, :, :) = 0.0_dp
309 nc_a = SIZE(left%contraction, 1)
310 DO ib = 1, SIZE(b%atoms)
311 DO sb = 1, SIZE(b%atoms(ib)%shells)
312 associate(right => b%atoms(ib)%shells(sb))
313 cutoff = left%radius + right%radius
314 disp(:) = matmul(a%inverse, right%center - left%center)
315 DO i = 1, 3
316 bounds(i) = cutoff*norm2(a%inverse(i, :))
317 END DO
318 lo(:) = ceiling(-disp - bounds)
319 hi(:) = floor(-disp + bounds)
320 WHERE (a%periodic == 0)
321 lo = 0
322 hi = 0
323 END WHERE
324 nc_b = SIZE(right%contraction, 1)
325 ALLOCATE (cosine(nc_a, nc_b), sine(nc_a, nc_b), block(left%count, right%count))
326 block(:, :) = 0.0_dp
327 DO k = lo(3), hi(3)
328 DO j = lo(2), hi(2)
329 DO i = lo(1), hi(1)
330 image(:) = [i, j, k]
331 rb(:) = right%center + matmul(a%cell, real(image, dp))
332 IF (norm2(rb - left%center) > cutoff) cycle
333 CALL cossin(left%lmax, SIZE(left%exponent), left%exponent, left%radii, left%lmin, &
334 right%lmax, SIZE(right%exponent), right%exponent, right%radii, right%lmin, &
335 left%center, rb, q, cosine, sine)
336 phase = exp(cmplx(0.0_dp, 2.0_dp*pi*dot_product(kb, real(image, dp)), dp))
337 block(:, :) = block + phase*matmul(transpose(left%contraction), &
338 matmul(cmplx(cosine, -sine, dp), right%contraction))
339 END DO
340 END DO
341 END DO
342 DO s = 1, a%nspin
343 bf = right%first + (s - 1)*b%nao
344 oc(:, :, s) = oc(:, :, s) + matmul(block, b%coefficients(bf:bf + right%count - 1, :))
345 END DO
346 DEALLOCATE (cosine, sine, block)
347 END associate
348 END DO
349 END DO
350 DO s = 1, a%nspin
351 af = left%first + (s - 1)*a%nao
352 atom_link(:, :) = atom_link + matmul(conjg(transpose(a%coefficients(af:af + left%count - 1, :))), oc(:, :, s))
353 END DO
354 DEALLOCATE (oc)
355 END associate
356 END DO
357 END SUBROUTINE atom_overlap
358
359! **************************************************************************************************
360!> \brief Check AO-metric normalization and separation at every selected/excluded boundary.
361!> \param a Physical frame to validate
362!> \param metric_tol Maximum entrywise error in C^dagger S C minus identity
363!> \param gap_tol Minimum sampled selected/excluded-band separation in hartree
364!> \param metric_error Observed normalization error
365!> \param gap Smallest sampled selected/excluded-band separation in hartree
366! **************************************************************************************************
367 SUBROUTINE check_snapshot(a, metric_tol, gap_tol, metric_error, gap)
368 TYPE(snapshot_type), INTENT(IN) :: a
369 REAL(kind=dp), INTENT(IN) :: metric_tol, gap_tol
370 REAL(kind=dp), INTENT(OUT) :: metric_error, gap
371
372 INTEGER :: i
373 LOGICAL :: selected(size(a%energies))
374 COMPLEX(KIND=dp) :: metric(a%rank, a%rank)
375
376 CALL snapshot_overlap(a, a, a%k, a%k, metric)
377 DO i = 1, a%rank
378 metric(i, i) = metric(i, i) - 1.0_dp
379 END DO
380 metric_error = maxval(abs(metric))
381 IF (.NOT. ieee_is_finite(metric_error) .OR. metric_error > metric_tol) THEN
382 cpabort('Frame is not AO-metric normalized')
383 END IF
384 selected(:) = .false.
385 selected(a%bands) = .true.
386 gap = huge(1.0_dp)
387 DO i = 1, SIZE(a%energies) - 1
388 IF (selected(i) .NEQV. selected(i + 1)) gap = min(gap, a%energies(i + 1) - a%energies(i))
389 END DO
390 IF (gap == huge(1.0_dp) .OR. gap <= gap_tol) THEN
391 cpabort('No verified sampled subspace isolation')
392 END IF
393 END SUBROUTINE check_snapshot
394
395! **************************************************************************************************
396!> \brief Check explicitly prescribed atom permutation, periodic translations and basis at a seam.
397!> \param a Start frame
398!> \param b Endpoint frame
399!> \param permutation One-based endpoint-atom to start-atom map
400!> \param tolerance Absolute tolerance for geometry and basis agreement
401! **************************************************************************************************
402 SUBROUTINE check_snapshot_seam(a, b, permutation, tolerance)
403 TYPE(snapshot_type), INTENT(IN) :: a, b
404 INTEGER, INTENT(IN) :: permutation(:)
405 REAL(kind=dp), INTENT(IN) :: tolerance
406
407 INTEGER :: i, j, n, s
408 REAL(kind=dp) :: delta(3), shift(3)
409
410 n = SIZE(a%atoms)
411 IF (SIZE(b%atoms) /= n .OR. SIZE(permutation) /= n) THEN
412 cpabort('Seam atom count mismatch')
413 END IF
414 IF (any(permutation < 1) .OR. any(permutation > n)) THEN
415 cpabort('Invalid seam permutation')
416 END IF
417 DO i = 1, n
418 IF (count(permutation == i) /= 1) THEN
419 cpabort('Seam atom map is not a permutation')
420 END IF
421 END DO
422 IF (maxval(abs(a%cell - b%cell)) > tolerance .OR. any(a%periodic /= b%periodic)) THEN
423 cpabort('Seam cell mismatch')
424 END IF
425 DO i = 1, n
426 j = permutation(i)
427 associate(left => a%atoms(j), right => b%atoms(i))
428 IF (left%kind /= right%kind .OR. left%count /= right%count) THEN
429 cpabort('Seam changes atom kind or AO count')
430 END IF
431 IF (SIZE(left%shells) /= SIZE(right%shells)) THEN
432 cpabort('Seam changes basis')
433 END IF
434 delta(:) = right%center - left%center
435 shift(:) = anint(matmul(a%inverse, delta))
436 WHERE (a%periodic == 0) shift = 0.0_dp
437 IF (maxval(abs(delta - matmul(a%cell, shift))) > tolerance) THEN
438 cpabort('Geometry does not close at seam')
439 END IF
440 DO s = 1, SIZE(left%shells)
441 associate(x => left%shells(s), y => right%shells(s))
442 IF (x%count /= y%count .OR. x%lmin /= y%lmin .OR. x%lmax /= y%lmax) THEN
443 cpabort('Seam changes shell')
444 END IF
445 IF (any(shape(x%contraction) /= shape(y%contraction))) THEN
446 cpabort('Seam changes contractions')
447 END IF
448 IF (SIZE(x%exponent) /= SIZE(y%exponent)) THEN
449 cpabort('Seam changes primitives')
450 END IF
451 IF (maxval(abs(x%radii - y%radii)) > tolerance .OR. abs(x%radius - y%radius) > tolerance) THEN
452 cpabort('Seam changes Gaussian screening radii')
453 END IF
454 IF (maxval(abs(x%exponent - y%exponent)) > tolerance .OR. &
455 maxval(abs(x%contraction - y%contraction)) > tolerance) THEN
456 cpabort('Seam changes physical basis')
457 END IF
458 END associate
459 END DO
460 END associate
461 END DO
462 END SUBROUTINE check_snapshot_seam
463END MODULE topology_snapshot
Calculation of the moment integrals over Cartesian Gaussian-type functions.
Definition ai_moments.F:17
subroutine, public cossin(la_max_set, npgfa, zeta, rpgfa, la_min_set, lb_max, npgfb, zetb, rpgfb, lb_min, rac, rbc, kvec, cosab, sinab, dcosab, dsinab)
...
Definition ai_moments.F:162
Definition atom.F:9
Utility routines to open and close files. Tracking of preconnections.
Definition cp_files.F:16
subroutine, public open_file(file_name, file_status, file_form, file_action, file_position, file_pad, unit_number, debug, skip_get_unit_number, file_access)
Opens the requested file using a free unit number.
Definition cp_files.F:311
subroutine, public close_file(unit_number, file_status, keep_preconnection)
Close an open file given by its logical unit number. Optionally, keep the file and unit preconnected.
Definition cp_files.F:122
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Provides Cartesian and spherical orbital pointers and indices.
subroutine, public init_orbital_pointers(maxl)
Initialize or update the orbital pointers.
integer, dimension(:, :), allocatable, public indco
Reader and physical moving-basis links for version-1 topology snapshots.
subroutine, public check_snapshot_seam(a, b, permutation, tolerance)
Check explicitly prescribed atom permutation, periodic translations and basis at a seam.
subroutine, public snapshot_overlap(a, b, ka, kb, overlap)
Apply screened Gaussian cross-geometry operator by atom blocks, without a dense AO matrix.
subroutine, public check_snapshot(a, metric_tol, gap_tol, metric_error, gap)
Check AO-metric normalization and separation at every selected/excluded boundary.
subroutine, public read_snapshot(filename, point, state)
Read a single point, retaining only its states; reject malformed/truncated exports.