forked from AmbaPant/mantid
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathIndexSatellitePeaks.py
134 lines (103 loc) · 5.62 KB
/
IndexSatellitePeaks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
# NScD Oak Ridge National Laboratory, European Spallation Source,
# Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
# SPDX - License - Identifier: GPL - 3.0 +
import numpy as np
from scipy.spatial import KDTree
import fractional_indexing as indexing
from mantid.kernel import Direction, V3D
from mantid.api import (IPeaksWorkspaceProperty, PythonAlgorithm, AlgorithmFactory)
import mantid.simpleapi as api
class IndexSatellitePeaks(PythonAlgorithm):
def category(self):
return 'Crystal\\Peaks'
def seeAlso(self):
return [ "FindSatellitePeaks" ]
def name(self):
return "IndexSatellitePeaks"
def summary(self):
return "Algorithm for indexing satellite peaks in superspace"
def PyInit(self):
self.declareProperty(IPeaksWorkspaceProperty(name="NuclearPeaks",
defaultValue="",
direction=Direction.Input),
doc="Main integer HKL peaks. Q vectors will be calculated relative to these peaks.")
self.declareProperty(IPeaksWorkspaceProperty(name="SatellitePeaks",
defaultValue="",
direction=Direction.Input),
doc="Positions of satellite peaks with fractional \
HKL coordinates")
self.declareProperty(IPeaksWorkspaceProperty(name="OutputWorkspace",
defaultValue="",
direction=Direction.Output),
doc="The indexed satellite peaks. This will be a \
table workspace with miller indicies h, k, l, m1, \
m2, ..., mn.")
self.declareProperty('Tolerance', 0.3, direction=Direction.Input,
doc="Tolerance on the noise of the q vectors")
self.declareProperty('NumOfQs', 1, direction=Direction.Input,
doc="Number of independant q vectors")
self.declareProperty('ClusterThreshold', 1.5, direction=Direction.Input,
doc="Threshold for automaticallty deciding on the number of q vectors to use. If NumOfQs found is set then this \
is property is ignored.")
def PyExec(self):
tolerance = self.getProperty("Tolerance").value
k = int(self.getProperty("NumOfQs").value)
nuclear = self.getProperty("NuclearPeaks").value
satellites = self.getProperty("SatellitePeaks").value
cluster_threshold = self.getProperty("ClusterThreshold").value
n_trunc_decimals = int(np.ceil(abs(np.log10(tolerance))))
if nuclear.getNumberPeaks() == 0:
raise RuntimeError("The NuclearPeaks parameter must have at least one peak")
if satellites.getNumberPeaks() == 0:
raise RuntimeError("The SatellitePeaks parameter must have at least one peak")
nuclear_hkls = indexing.get_hkls(nuclear)
sats_hkls = indexing.get_hkls(satellites)
qs = indexing.find_q_vectors(nuclear_hkls, sats_hkls)
self.log().notice("K value is {}".format(k))
k = None if k == -1 else k
clusters, k = indexing.cluster_qs(qs, k=k, threshold=cluster_threshold)
qs = indexing.average_clusters(qs, clusters)
qs = indexing.trunc_decimals(qs, n_trunc_decimals)
qs = indexing.sort_vectors_by_norm(qs)
self.log().notice("Q vectors are: \n{}".format(qs))
indices = indexing.index_q_vectors(qs, tolerance)
ndim = indices.shape[1] + 3
hkls = indexing.find_nearest_integer_peaks(nuclear_hkls, sats_hkls)
hklm = np.zeros((hkls.shape[0], ndim))
hklm[:, :3] = np.round(hkls)
raw_qs = hkls - sats_hkls
peak_map = KDTree(qs)
for i, q in enumerate(raw_qs):
distance, index = peak_map.query(q, k=1)
hklm[i, 3:] = indices[index]
indexed = self.create_indexed_peaksworkspace(satellites, qs, hklm)
self.setProperty("OutputWorkspace", indexed)
def create_indexed_peaksworkspace(self, fractional_peaks, qs, hklm):
"""Create a PeaksWorkspace that contains indexed peak data.
:param fractional_peaks: the peaks workspace containing peaks with
fractional HKL values.
:param qs: The set of modulation vectors determined
:param hklm: the new higher dimensional miller indices to add.
:returns: a peaks workspace with the indexed peak data
"""
# pad to 6 columns so we can assume a (hkl) (mnp) layout
hklm = np.pad(hklm, pad_width=(0, 6 - hklm.shape[1]), mode='constant',
constant_values=0)
indexed = api.CloneWorkspace(fractional_peaks, StoreInADS=False)
# save modulation vectors. ensure qs has 3 rows
qs = np.pad(qs, pad_width=((0, 3 - qs.shape[0]), (0, 0)), mode='constant',
constant_values=0)
lattice = fractional_peaks.sample().getOrientedLattice()
lattice.setModVec1(V3D(*qs[0]))
lattice.setModVec2(V3D(*qs[1]))
lattice.setModVec3(V3D(*qs[2]))
# save indices
for row, peak in enumerate(indexed):
row_indices = hklm[row]
peak.setHKL(*row_indices[:3])
peak.setIntMNP(V3D(*row_indices[3:]))
return indexed
AlgorithmFactory.subscribe(IndexSatellitePeaks)