Skip to content

Commit c47e4b5

Browse files
Merge pull request #38621 from peterfpeterson/abins_missing_xs_ornlnext
Abins: improve handling of isotopic cross-sections
2 parents b53674e + 2f39608 commit c47e4b5

File tree

9 files changed

+220
-126
lines changed

9 files changed

+220
-126
lines changed

Framework/PythonInterface/plugins/algorithms/Abins.py

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77

88
from typing import Dict
99

10+
import numpy as np
11+
1012
from mantid.api import AlgorithmFactory, FileAction, FileProperty, PythonAlgorithm, Progress
1113
from mantid.api import WorkspaceFactory, AnalysisDataService
1214

1315
# noinspection PyProtectedMember
1416
from mantid.simpleapi import ConvertUnits, GroupWorkspaces, Load
1517
from mantid.kernel import Direction, StringListValidator
1618
import abins
17-
from abins.abinsalgorithm import AbinsAlgorithm
19+
from abins.abinsalgorithm import AbinsAlgorithm, AtomInfo
1820

1921

2022
# noinspection PyPep8Naming,PyMethodMayBeStatic
@@ -184,14 +186,13 @@ def PyExec(self):
184186
self.setProperty("OutputWorkspace", self._out_ws_name)
185187
prog_reporter.report("Group workspace with all required dynamical structure factors has been constructed.")
186188

187-
def _fill_s_workspace(self, s_points=None, workspace=None, protons_number=None, nucleons_number=None):
189+
def _fill_s_workspace(self, *, s_points: np.ndarray, workspace: str, species: AtomInfo | None = None):
188190
"""
189191
Puts S into workspace(s).
190192
191193
:param s_points: dynamical factor for the given atom
192194
:param workspace: workspace to be filled with S
193-
:param protons_number: number of protons in the given type fo atom
194-
:param nucleons_number: number of nucleons in the given type of atom
195+
:param species: atom/isotope identity and data
195196
"""
196197

197198
from abins.constants import FUNDAMENTALS, ONE_DIMENSIONAL_INSTRUMENTS, ONE_DIMENSIONAL_SPECTRUM
@@ -201,15 +202,11 @@ def _fill_s_workspace(self, s_points=None, workspace=None, protons_number=None,
201202

202203
# only FUNDAMENTALS [data is 2d with one row]
203204
if s_points.shape[0] == FUNDAMENTALS:
204-
self._fill_s_1d_workspace(
205-
s_points=s_points[0], workspace=workspace, protons_number=protons_number, nucleons_number=nucleons_number
206-
)
205+
self._fill_s_1d_workspace(s_points=s_points[0], workspace=workspace, species=species)
207206

208207
# total workspaces [data is 1d vector]
209208
elif len(s_points.shape) == ONE_DIMENSIONAL_SPECTRUM:
210-
self._fill_s_1d_workspace(
211-
s_points=s_points, workspace=workspace, protons_number=protons_number, nucleons_number=nucleons_number
212-
)
209+
self._fill_s_1d_workspace(s_points=s_points, workspace=workspace, species=species)
213210

214211
# quantum order events (fundamentals or overtones + combinations for the given order)
215212
# [data is 2d table of S with a row for each quantum order]
@@ -221,28 +218,19 @@ def _fill_s_workspace(self, s_points=None, workspace=None, protons_number=None,
221218
wrk_name = f"{workspace}_quantum_event_{n + 1}"
222219
partial_wrk_names.append(wrk_name)
223220

224-
self._fill_s_1d_workspace(
225-
s_points=s_points[n], workspace=wrk_name, protons_number=protons_number, nucleons_number=nucleons_number
226-
)
221+
self._fill_s_1d_workspace(s_points=s_points[n], workspace=wrk_name, species=species)
227222

228223
GroupWorkspaces(InputWorkspaces=partial_wrk_names, OutputWorkspace=workspace)
229224

230-
def _fill_s_1d_workspace(self, s_points=None, workspace=None, protons_number=None, nucleons_number=None):
225+
def _fill_s_1d_workspace(self, *, s_points: np.ndarray, workspace: str, species: AtomInfo | None = None):
231226
"""
232227
Puts 1D S into workspace.
233-
:param protons_number: number of protons in the given type of atom
234-
:param nucleons_number: number of nucleons in the given type of atom
235228
:param s_points: dynamical factor for the given atom
236229
:param workspace: workspace to be filled with S
230+
:param species: atom/isotope identity and data
237231
"""
238-
if protons_number is not None:
239-
s_points = (
240-
s_points
241-
* self._scale
242-
* self.get_cross_section(
243-
scattering=self._scale_by_cross_section, protons_number=protons_number, nucleons_number=nucleons_number
244-
)
245-
)
232+
if species is not None:
233+
s_points = s_points * self._scale * self.get_cross_section(scattering=self._scale_by_cross_section, species=species)
246234
dim = 1
247235
length = s_points.size
248236

Framework/PythonInterface/plugins/algorithms/Abins2D.py

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
import numbers
99
from typing import Dict
1010

11+
import numpy as np
12+
1113
from mantid.api import AlgorithmFactory, PythonAlgorithm, Progress
1214
from mantid.api import WorkspaceFactory, AnalysisDataService
1315
from mantid.kernel import logger
1416

1517
# noinspection PyProtectedMember
1618
from mantid.simpleapi import GroupWorkspaces
1719
import abins
18-
from abins.abinsalgorithm import AbinsAlgorithm
20+
from abins.abinsalgorithm import AbinsAlgorithm, AtomInfo
1921

2022

2123
# noinspection PyPep8Naming,PyMethodMayBeStatic
@@ -184,14 +186,13 @@ def PyExec(self):
184186
self.setProperty("OutputWorkspace", self._out_ws_name)
185187
prog_reporter.report("Group workspace with all required dynamical structure factors has been constructed.")
186188

187-
def _fill_s_workspace(self, s_points=None, workspace=None, protons_number=None, nucleons_number=None):
189+
def _fill_s_workspace(self, *, s_points=None, workspace: str, species: AtomInfo | None = None):
188190
"""
189191
Puts S into workspace(s).
190192
191193
:param s_points: dynamical factor for the given atom
192194
:param workspace: workspace to be filled with S
193-
:param protons_number: number of protons in the given type fo atom
194-
:param nucleons_number: number of nucleons in the given type of atom
195+
:param species: Atom/isotope data
195196
"""
196197
from abins.constants import FUNDAMENTALS, TWO_DIMENSIONAL_INSTRUMENTS
197198

@@ -200,15 +201,11 @@ def _fill_s_workspace(self, s_points=None, workspace=None, protons_number=None,
200201

201202
# only FUNDAMENTALS [data is 3d with length 1 in axis 0]
202203
if s_points.shape[0] == FUNDAMENTALS:
203-
self._fill_s_2d_workspace(
204-
s_points=s_points[0], workspace=workspace, protons_number=protons_number, nucleons_number=nucleons_number
205-
)
204+
self._fill_s_2d_workspace(s_points=s_points[0], workspace=workspace, species=species)
206205

207206
# total workspaces [data is 2d array of S]
208207
elif s_points.shape[0] == abins.parameters.instruments[self._instrument.get_name()]["q_size"]:
209-
self._fill_s_2d_workspace(
210-
s_points=s_points, workspace=workspace, protons_number=protons_number, nucleons_number=nucleons_number
211-
)
208+
self._fill_s_2d_workspace(s_points=s_points, workspace=workspace, species=species)
212209

213210
# Multiple quantum order events [data is 3d table of S using axis 0 for quantum orders]
214211
else:
@@ -219,9 +216,7 @@ def _fill_s_workspace(self, s_points=None, workspace=None, protons_number=None,
219216
wrk_name = f"{workspace}_quantum_event_{n + 1}"
220217
partial_wrk_names.append(wrk_name)
221218

222-
self._fill_s_2d_workspace(
223-
s_points=s_points[n], workspace=wrk_name, protons_number=protons_number, nucleons_number=nucleons_number
224-
)
219+
self._fill_s_2d_workspace(s_points=s_points[n], workspace=wrk_name, species=species)
225220

226221
GroupWorkspaces(InputWorkspaces=partial_wrk_names, OutputWorkspace=workspace)
227222

@@ -232,14 +227,12 @@ def _create_dummy_workspace(self, name):
232227
AnalysisDataService.addOrReplace(name, wrk)
233228
return wrk
234229

235-
def _fill_s_2d_workspace(self, s_points=None, workspace=None, protons_number=None, nucleons_number=None):
230+
def _fill_s_2d_workspace(self, *, s_points: np.ndarray, workspace: str, species: AtomInfo | None = None):
236231
from mantid.api import NumericAxis
237232
from abins.constants import MILLI_EV_TO_WAVENUMBER
238233

239-
if protons_number is not None:
240-
s_points = s_points * self.get_cross_section(
241-
scattering=self._scale_by_cross_section, protons_number=protons_number, nucleons_number=nucleons_number
242-
)
234+
if species is not None:
235+
s_points = s_points * self.get_cross_section(scattering=self._scale_by_cross_section, species=species)
243236

244237
n_q_values, n_freq_bins = s_points.shape
245238
n_q_bins = self._q_bins.size
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
- The identification of isotopes has been improved in Abins/Abins2D.
2+
3+
- Previously, a species would only be identified as the standard
4+
isotopic mixture if the mass is very close to the Mantid reference
5+
data. In some cases the values used by external phonon calculators
6+
are significantly different and this could lead to misassignment.
7+
(e.g. Zn in CASTEP.) Now, Abins will initially choose the _nearest_
8+
mass option between an isotope and the standard isotopic mixture.
9+
- Many isotopes lack cross-section data and would lead to NaN
10+
intensities. Now, if a NaN cross-section is identified Abins
11+
will either use the standard mixture data (if the mass is within
12+
0.01) or raise an error.

scripts/abins/abins2.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77

88
from typing import Dict, Optional
99

10+
import numpy as np
11+
1012
from mantid.api import AlgorithmFactory, PythonAlgorithm, Progress
1113
from mantid.api import WorkspaceFactory, AnalysisDataService
1214

1315
# noinspection PyProtectedMember
1416
from mantid.simpleapi import ConvertUnits, GroupWorkspaces
1517
import abins
1618
from abins.abinsalgorithm import AbinsAlgorithm
19+
from abins.atominfo import AtomInfo
1720
from abins.logging import get_logger, Logger
1821

1922

@@ -170,14 +173,13 @@ def PyExec(self):
170173
self.setProperty("OutputWorkspace", self._out_ws_name)
171174
prog_reporter.report("Group workspace with all required dynamical structure factors has been constructed.")
172175

173-
def _fill_s_workspace(self, s_points=None, workspace=None, protons_number=None, nucleons_number=None):
176+
def _fill_s_workspace(self, *, s_points: np.ndarray, workspace: str, species: AtomInfo | None = None) -> None:
174177
"""
175178
Puts S into workspace(s).
176179
177180
:param s_points: dynamical factor for the given atom
178-
:param workspace: workspace to be filled with S
179-
:param protons_number: number of protons in the given type fo atom
180-
:param nucleons_number: number of nucleons in the given type of atom
181+
:param workspace: workspace to be filled with S
182+
:param species: Element/isotope data
181183
"""
182184

183185
from abins.constants import FUNDAMENTALS, ONE_DIMENSIONAL_INSTRUMENTS, ONE_DIMENSIONAL_SPECTRUM
@@ -187,15 +189,11 @@ def _fill_s_workspace(self, s_points=None, workspace=None, protons_number=None,
187189

188190
# only FUNDAMENTALS [data is 2d with one row]
189191
if s_points.shape[0] == FUNDAMENTALS:
190-
self._fill_s_1d_workspace(
191-
s_points=s_points[0], workspace=workspace, protons_number=protons_number, nucleons_number=nucleons_number
192-
)
192+
self._fill_s_1d_workspace(s_points=s_points[0], workspace=workspace, species=species)
193193

194194
# total workspaces [data is 1d vector]
195195
elif len(s_points.shape) == ONE_DIMENSIONAL_SPECTRUM:
196-
self._fill_s_1d_workspace(
197-
s_points=s_points, workspace=workspace, protons_number=protons_number, nucleons_number=nucleons_number
198-
)
196+
self._fill_s_1d_workspace(s_points=s_points, workspace=workspace, species=species)
199197

200198
# quantum order events (fundamentals or overtones + combinations for the given order)
201199
# [data is 2d table of S with a row for each quantum order]
@@ -207,24 +205,20 @@ def _fill_s_workspace(self, s_points=None, workspace=None, protons_number=None,
207205
wrk_name = f"{workspace}_quantum_event_{n + 1}"
208206
partial_wrk_names.append(wrk_name)
209207

210-
self._fill_s_1d_workspace(
211-
s_points=s_points[n], workspace=wrk_name, protons_number=protons_number, nucleons_number=nucleons_number
212-
)
208+
self._fill_s_1d_workspace(s_points=s_points[n], workspace=wrk_name, species=species)
213209

214210
GroupWorkspaces(InputWorkspaces=partial_wrk_names, OutputWorkspace=workspace)
215211

216-
def _fill_s_1d_workspace(self, s_points=None, workspace=None, protons_number=None, nucleons_number=None):
212+
def _fill_s_1d_workspace(self, s_points=None, workspace=None, species: AtomInfo | None = None):
217213
"""
218214
Puts 1D S into workspace.
219215
:param protons_number: number of protons in the given type of atom
220216
:param nucleons_number: number of nucleons in the given type of atom
221217
:param s_points: dynamical factor for the given atom
222218
:param workspace: workspace to be filled with S
223219
"""
224-
if protons_number is not None:
225-
s_points = s_points * self.get_cross_section(
226-
scattering=self._scale_by_cross_section, protons_number=protons_number, nucleons_number=nucleons_number
227-
)
220+
if species is not None:
221+
s_points = s_points * self.get_cross_section(scattering=self._scale_by_cross_section, species=species)
228222
dim = 1
229223
length = s_points.size
230224

0 commit comments

Comments
 (0)