[ESSDIFFRACTION, BEER] Adapt d-spacing binning
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1
- Forks
- 3
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 17
Description
For binning in d-spacing, provide only the number of bins and calculate the min and max in the notebook for the different banks using Premek’s code.
def read_info_and_get_bins(wf: BeerPowderMcStasWorkflow, size_d: int = 3000, size_tth: int = 500) -> dict:
"""
Function that loads Sciline workflow upto RawDetector and return important
information used for further processing and creates d-spacing, two theta
and Q bins. Number of bins can be modified for d and Q and or two theta.
Parameters
----------
data_path : str
Path to the NeXus file without the file name but with the directory delimiter
size_d : int, optional
Number of the d-spacing and Q bins. Default is 3000.
size_tth : int, optional
Number of the two theta bins. Default is 500.
Returns
-------
dict
Dictionary containing the following variables
- 'wave_min/max' - values of accepted wavwlengthes
- 't_det_min/max' - values of accepted TOF
- 'd_lamda' - size of the accepted wavelength range
and bank ['north', 'south'] decicated
- 'tth' - value of two theta angle of the bank
- 'd_bank' - position name of the bank S1-3, N1-3
- 'd_bins' - scipp.DataArray of d-spacing bins
- 'tth_bins' - scipp.DataArray of two theta bins
- 'q_bins' - scipp.DataArray of Q bins
"""
# setup of the result dictionary
result = {}
# reading the Nexus file
print('Loading "RawDetector" for the Nexus file ... ', end='', flush=True)
start = time.perf_counter()
bank = DetectorBank.north
wf[DetectorBank] = bank
data = wf.compute(RawDetector[SampleRun])
duration = time.perf_counter() - start
print(f'DONE in {_format_time(duration)}')
print('')
print('Measurement information summary')
print('-------------------------------')
# adjsting the common variables
t = data.coords['nominal_time_at_chopper'] - sc.scalar(1.6, unit='ms').to(unit='s')
dist = data.coords['source_to_wavelength_definition_chopper_distance']
wave = (t * sc.constants.h / sc.constants.m_n / dist).to(unit='angstroms')
print(f'Source chopper distance: {dist.value:0.3f} [{dist.unit}]')
print(f'Nominal wavelength: {wave.value:0.1f} [{wave.unit}]')
print(f'Time when neutrons at chopper distance: {t.value:0.5f} [{t.unit}]')
det = sc.mean(data.coords['moderator_to_detector_distance'])-dist
d_lambda = 1.73*sc.Unit('angstrom')
result['d_lambda'] = d_lambda
print(f'Acceptance wavelength range fixed to: {d_lambda.value} [{d_lambda.unit}]')
wave_min = wave - d_lambda/2
wave_max = wave + d_lambda/2
t_det_min = (sc.constants.m_n / sc.constants.h * det * wave_min).to(unit='ms')
t_det_max = (sc.constants.m_n / sc.constants.h * det * wave_max).to(unit='ms')
L2 = sc.norm(sc.mean(data.coords['position'])-data.coords['sample_position'])
L1 = det - L2
result['wave_min'] = wave_min
result['wave_max'] = wave_max
result['t_det_min'] = t_det_min
result['t_det_max'] = t_det_max
print(f'Source-sample distance (L1): {L1.value:0.3f} [{L1.unit}]')
print(f'Sample-detector distance (L2): {L2.value:0.3f} [{L2.unit}]')
print(f'Source-detector distance (L1+L2): {det.value:0.3f} [{det.unit}]')
print(f'Minimal wavelength accepted: {wave_min.value:0.2f} [{wave_min.unit}]')
print(f'Maximal wavelength accepted: {wave_max.value:0.2f} [{wave_max.unit}]')
print(f'Wave_min travel time: {t_det_min.value:0.5f} [{t_det_min.unit}]')
print(f'Wave_max travel time: {t_det_max.value:0.5f} [{t_det_max.unit}]')
# setting the wavelength mask to get rid of the frame overlap
wf[WavelengthMask] = lambda wave: (wave_min > wave) | (wave_max < wave)
# getting info about the detector positions, expecting that the McStas model
# simulate the detectors in 180 deg alignement, ie. is one 45 deg then other -135 deg,
# when one 90 the second -90, etc.
det_pos = (sc.norm(data.coords['sample_position']) -
sc.norm(sc.mean(data.coords['detector_position']))).value
if det_pos < -0.5:
tth = {'north': 45, 'south': 135}
d_bank = {'north': 'N1', 'south': 'S3'}
elif det_pos > 0.5:
tth = {'north': 135, 'south': 45}
d_bank = {'north': 'N3', 'south': 'S1'}
else:
tth = {'north': 90, 'south': 90}
d_bank = {'north': 'N2', 'south': 'S2'}
print('')
print('Individual detector information')
d_min = {}
d_max = {}
q_min = {}
q_max = {}
d_bins = {}
tth_bins = {}
q_bins = {}
tth_bins = {}
for bank in [DetectorBank.north, DetectorBank.south]:
print('-------------------------------')
n = bank.name
print(f'"{n}" detector is {d_bank[n]} at {tth[n]} [deg]')
d_min[n] = wave_min / 2 / sc.sin(sc.scalar((tth[n] + 15)/2, unit='deg'))
d_max[n] = wave_max / 2 / sc.sin(sc.scalar((tth[n] - 15)/2, unit='deg'))
d_min[n].value = _truncate(d_min[n].value, 2)
d_max[n].value = _truncate(d_max[n].value, 2)
print(f'Minimal d-spacing accepted: {d_min[n].value:0.2f} [{d_min[n].unit}]')
print(f'Maximal d-spacing accepted: {d_max[n].value:0.2f} [{d_max[n].unit}]')
d_bins[n] = sc.linspace(dim='dspacing', start=d_min[n], stop=d_max[n], num=size_d, unit='angstrom')
print(f'D-spacing bins [{len(d_bins[n])}] between {d_min[n].value} and {d_max[n].value} ' +
f'with step {(d_bins[n][1]-d_bins[n][0]).value:0.6f} [{d_max[n].unit}]')
tth_bins[n] = sc.linspace(dim='two_theta', start=tth[n] - 15, stop=tth[n] + 15, num=size_tth, unit='deg')
print(f'TwoTheta bins [{len(tth_bins[n])}] between {tth_bins[n][0].value:0.1f} and {tth_bins[n][-1].value:0.1f} ' +
f'with step {(tth_bins[n][1]-tth_bins[n][0]).value:0.4f} [{tth_bins[n].unit}]')
# QBins is not part of the BEER Sciline workflow
# The calculations related to Q below will be used for the final 1D Q-plots
q_min[n] = 2 * np.pi / d_max[n]
q_max[n] = 2 * np.pi / d_min[n]
q_min[n].value = _truncate(q_min[n].value, 2)
q_max[n].value = _truncate(q_max[n].value, 2)
q_bins[n] = sc.linspace(dim='Q', start=q_min[n], stop=q_max[n], num=size_d, unit='1/angstrom')
print(f'Q bins [{len(q_bins[n])}] between {q_min[n].value} and {q_max[n].value} ' +
f'with step {(q_bins[n][1]-q_bins[n][0]).value:0.6f} [{q_max[n].unit}]')
print('-------------------------------')
result['d_bins'] = d_bins
result['tth_bins'] = tth_bins
result['q_bins'] = q_bins
result['d_bank'] = d_bank
result['tth'] = tth
return result
This function describes all necessary steps needed before starting the full workflow.
We have to set up the mask, get proper bins depending on the detector position…. All this could also be part of the workflow.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the read_info_and_get_bins function shown in the issue and trace how the full BEER Sciline workflow consumes its mask and d-spacing bins. Verify how detector banks and positions are represented before changing the interface to accept only bin counts. Done means the wavelength mask and per-bank d-spacing bounds are calculated in the notebook or workflow, with both banks receiving the requested number of bins.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- data
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100