Skip to content

Commit fb03525

Browse files
committed
attempt testing unit cell multiples. not working yet
1 parent 434df29 commit fb03525

File tree

2 files changed

+120
-3
lines changed

2 files changed

+120
-3
lines changed

cctbx/crystal/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
import math
2525
from scitbx import matrix
2626
import scitbx.cubicle_neighbors
27-
from cctbx.uctbx.near_minimum import find_near_minimum_settings, cell_distance
27+
from cctbx.uctbx.near_minimum import find_near_minimum_settings, \
28+
find_near_minimum_settings_multiples, cell_distance
2829
import numpy as np
2930
cubicles_max_memory_allocation_set(
3031
number_of_bytes=scitbx.cubicle_neighbors.cubicles_max_memory_allocation_get())
@@ -545,7 +546,7 @@ def nearest_setting(self, other,
545546
if not hasattr(self, '_near_minimum_cache') or \
546547
getattr(self, '_near_minimum_cache_params', None) != (length_tolerance, angle_tolerance):
547548
mc_self = self.minimum_cell()
548-
settings = find_near_minimum_settings(
549+
settings = find_near_minimum_settings_multiples(
549550
mc_self.unit_cell(),
550551
length_tolerance=length_tolerance,
551552
angle_tolerance=angle_tolerance
@@ -578,7 +579,7 @@ def nearest_setting(self, other,
578579
# Convention: P from find_near_minimum_settings satisfies P = inv(cb.c().r())
579580
# So to construct cb_op, we need to pass P^{-1}
580581
P = settings[best_idx]['P']
581-
P_flat = tuple(int(x) for x in P.ravel())
582+
P_flat = tuple(float(x) for x in P.ravel())
582583

583584
rot_mx = sgtbx.rot_mx(P_flat)
584585
cb_near = sgtbx.change_of_basis_op(sgtbx.rt_mx(rot_mx))

cctbx/uctbx/near_minimum.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,119 @@ def find_near_minimum_settings(
291291
results.sort(key=lambda x: sum(x['cell'][:3]))
292292

293293
return results
294+
295+
296+
def find_near_minimum_settings_multiples(
297+
cell,
298+
length_tolerance=0.03,
299+
angle_tolerance=3.0,
300+
max_search_index=10,
301+
):
302+
"""
303+
Find nearly-reduced settings across order-2 super- and subcells.
304+
305+
This function extends find_near_minimum_settings by searching not only the
306+
input cell, but also 7 order-2 supercells and 7 order-2 subcells (14 total
307+
additional cells). This helps find alternative settings that might be missed
308+
due to reduction boundaries when the true cell might be a multiple or
309+
submultiple of the measured cell.
310+
311+
Parameters
312+
----------
313+
cell : tuple or unit_cell
314+
(a, b, c, alpha, beta, gamma) - axes in Angstrom, angles in degrees
315+
length_tolerance : float
316+
Fractional tolerance for length perturbations (e.g., 0.03 = 3%)
317+
angle_tolerance : float
318+
Tolerance for angle perturbations in degrees
319+
max_search_index : int
320+
Search lattice vectors up to +/-max_search_index
321+
322+
Returns
323+
-------
324+
list of dict
325+
Each dict contains:
326+
- 'P': transformation matrix (3x3 integer or float array) including
327+
the super/subcell transformation
328+
- 'cell': transformed cell parameters (tuple of 6 floats)
329+
- 'G': transformed metric tensor (3x3 array)
330+
"""
331+
# Handle cctbx unit_cell objects
332+
if hasattr(cell, 'parameters'):
333+
cell = cell.parameters()
334+
335+
# Define 7 standard order-2 supercell transformations (det = 2)
336+
supercell_transforms = [
337+
np.array([[2, 0, 0], [0, 1, 0], [0, 0, 1]]), # double a
338+
np.array([[1, 0, 0], [0, 2, 0], [0, 0, 1]]), # double b
339+
np.array([[1, 0, 0], [0, 1, 0], [0, 0, 2]]), # double c
340+
np.array([[1, 1, 0], [-1, 1, 0], [0, 0, 1]]), # C-face diagonal
341+
np.array([[1, 0, 1], [0, 1, 0], [-1, 0, 1]]), # B-face diagonal
342+
np.array([[1, 0, 0], [0, 1, 1], [0, -1, 1]]), # A-face diagonal
343+
np.array([[1, 1, 1], [-1, 1, 0], [-1, 0, 1]]), # body diagonal
344+
]
345+
346+
# Subcell transforms are the inverses of supercell transforms
347+
# (det = 1/2, map from original to smaller cell)
348+
subcell_transforms = [np.linalg.inv(S) for S in supercell_transforms]
349+
350+
all_results = []
351+
352+
# Process original cell
353+
original_results = find_near_minimum_settings(
354+
cell, length_tolerance, angle_tolerance, max_search_index
355+
)
356+
all_results.extend(original_results)
357+
358+
G_original = cell_to_metric_tensor(cell)
359+
360+
# Process supercells
361+
for S in supercell_transforms:
362+
# Transform to supercell: G_super = S^T @ G @ S
363+
G_super = S.T @ G_original @ S
364+
cell_super = metric_tensor_to_cell(G_super)
365+
366+
# Find near-minimum settings in the supercell
367+
super_results = find_near_minimum_settings(
368+
cell_super, length_tolerance, angle_tolerance, max_search_index
369+
)
370+
371+
# Compose transformations: P_total = S @ P_super
372+
for result in super_results:
373+
P_total = S @ result['P']
374+
G_total = P_total.T @ G_original @ P_total
375+
cell_total = metric_tensor_to_cell(G_total)
376+
377+
all_results.append({
378+
'P': P_total,
379+
'cell': cell_total,
380+
'G': G_total,
381+
})
382+
383+
# Process subcells
384+
for T in subcell_transforms:
385+
# Transform to subcell: G_sub = T^T @ G @ T
386+
G_sub = T.T @ G_original @ T
387+
cell_sub = metric_tensor_to_cell(G_sub)
388+
389+
# Find near-minimum settings in the subcell
390+
sub_results = find_near_minimum_settings(
391+
cell_sub, length_tolerance, angle_tolerance, max_search_index
392+
)
393+
394+
# Compose transformations: P_total = T @ P_sub
395+
for result in sub_results:
396+
P_total = T @ result['P']
397+
G_total = P_total.T @ G_original @ P_total
398+
cell_total = metric_tensor_to_cell(G_total)
399+
400+
all_results.append({
401+
'P': P_total,
402+
'cell': cell_total,
403+
'G': G_total,
404+
})
405+
406+
# Sort by sum of basis vector lengths
407+
all_results.sort(key=lambda x: sum(x['cell'][:3]))
408+
409+
return all_results

0 commit comments

Comments
 (0)