from os import PathLike
from textwrap import dedent
import pytest
from ewoksscxrd.tasks.createxdsinp import CreateXDSInp
def _task_inputs(output):
return {
"output": str(output),
"detector": "EIGER",
"nx": 2068,
"ny": 2162,
"qx": 0.075,
"qy": 0.075,
"beam": [1052, 1102],
"distance": 151.8,
"data_range": [1, 2],
"oscillation_range": 0.5,
"wavelength": 0.2846,
}
[docs]
def test_create_xds_inp_success(tmp_path: PathLike):
task = CreateXDSInp(inputs=_task_inputs(tmp_path / "cbf" / "frame_{index}.cbf"))
task.execute()
result = task.get_output_values()
expected_file = tmp_path / "cbf" / "XDS.INP"
assert result["saved_files_path"] == [str(expected_file)]
content = expected_file.read_text(encoding="ascii")
assert content == dedent(
"""\
!JOB= XYCORR INIT COLSPOT IDXREF DEFPIX INTEGRATE CORRECT
!JOB= XYCORR INIT COLSPOT IDXREF
JOB= XYCORR INIT COLSPOT IDXREF DEFPIX INTEGRATE CORRECT
NAME_TEMPLATE_OF_DATA_FRAMES= frame_?.cbf
SPACE_GROUP_NUMBER=0 ! 0 if unknown
!UNIT_CELL_CONSTANTS= 10.317 10.317 7.3378 90 90 120 ! put correct values if known
FRIEDEL'S_LAW=FALSE ! This acts only on the CORRECT step
FRACTION_OF_POLARIZATION=0.99
ROTATION_AXIS=0 -1 0
POLARIZATION_PLANE_NORMAL=0 1 0
DIRECTION_OF_DETECTOR_X-AXIS=1 0 0
DIRECTION_OF_DETECTOR_Y-AXIS=0 1 0
INCIDENT_BEAM_DIRECTION=0 0 1
OSCILLATION_RANGE= 0.5
OVERLOAD=100000000
DATA_RANGE= 1 2
SPOT_RANGE= 1 2
BACKGROUND_RANGE= 1 2
NX= 2068 NY= 2162 QX= 0.075000 QY= 0.075000
DETECTOR= EIGER
DETECTOR_DISTANCE= 151.8
ORGX= 1052 ORGY= 1102
X-RAY_WAVELENGTH=0.2846
UNTRUSTED_RECTANGLE= 513 514 0 3262
UNTRUSTED_RECTANGLE= 1028 1039 0 3262
UNTRUSTED_RECTANGLE= 1553 1554 0 3262
UNTRUSTED_RECTANGLE= 2068 2079 0 3262
UNTRUSTED_RECTANGLE= 2593 2594 0 3262
UNTRUSTED_RECTANGLE= 0 3108 512 549
UNTRUSTED_RECTANGLE= 0 3108 1062 1099
UNTRUSTED_RECTANGLE= 0 3108 1612 1649
UNTRUSTED_RECTANGLE= 0 3108 2162 2199
UNTRUSTED_RECTANGLE= 0 3108 2712 2749
SENSOR_THICKNESS=0.75 !mm
SILICON= 18.023 !1/mm
MINIMUM_NUMBER_OF_PIXELS_IN_A_SPOT=4 ! default of 6 is sometimes too high
MAXIMUM_NUMBER_OF_STRONG_PIXELS=18000 ! total number of strong pixels used for indexation
BACKGROUND_PIXEL=2.0 ! used by COLSPOT and INTEGRATE
SIGNAL_PIXEL=3.0 ! needs to be lager than BACKGROUND_PIXEL, specifies standard deviation, used in COLSPOT and INTEGRATE
REFINE(IDXREF)= CELL ORIENTATION AXIS ! BEAM POSITION
REFINE(INTEGRATE)= CELL ORIENTATION AXIS ! BEAM POSITION
REFINE(CORRECT)= CELL ORIENTATION AXIS ! BEAM POSITION
"""
)
[docs]
def test_create_xds_inp_with_overrides(tmp_path: PathLike):
inputs = _task_inputs(tmp_path / "cbf" / "frame_{index}.cbf")
inputs.update(
{
"filename": "custom_XDS.INP",
"detector": "PILATUS",
"job": " DEFPIX INTEGRATE CORRECT",
"job_comment_lines": [
"!JOB= XYCORR INIT COLSPOT IDXREF DEFPIX INTEGRATE CORRECT",
"!JOB= XYCORR INIT COLSPOT IDXREF ",
],
"library": "/home/opid27/lib/dectris-neggia.so",
"overload": 999999,
"space_group_number": 12,
"unit_cell_constants": "127.77 127.77 67.26 90.000 90.000 90.000",
"friedels_law": "TRUE",
"fraction_of_polarization": 0.5,
"trusted_region": [0.1, 1.2],
"polarization_plane_normal": [1.0, 0.0, 0.0],
"name_template_of_data_frames": "custom_????.cbf",
"spot_range": [2, 3],
"background_range": [1, 1],
"rotation_axis": [0.0, 1.0, 0.0],
"incident_beam_direction": [0.0, 0.0, -1.0],
"untrusted_rectangles": [[1, 2, 3, 4]],
"sensor_thickness": 1.0,
"silicon": 20.0,
"minimum_number_of_pixels_in_a_spot": 5,
"maximum_number_of_strong_pixels": 999,
"background_pixel": 1.5,
"signal_pixel": 4.5,
"refine_idxref": "AXIS",
"refine_integrate": "BEAM POSITION",
"refine_correct": "CELL",
"exclude_resolution_range": "3.93 3.87 !ice-ring at 3.897 Angstrom",
"include_resolution_range": "60 2.9 ! after CORRECT, insert high resol limit; re-run CORRECT",
"beam_divergence": 0.17577,
"beam_divergence_esd": 0.01758,
"reflecting_range": 1.87829,
"reflecting_range_esd": 0.26833,
"xds_extra": ["TEST= CUSTOM"],
}
)
task = CreateXDSInp(inputs=inputs)
task.execute()
content = (tmp_path / "cbf" / "custom_XDS.INP").read_text(encoding="ascii")
assert content == dedent(
"""\
!JOB= XYCORR INIT COLSPOT IDXREF DEFPIX INTEGRATE CORRECT
!JOB= XYCORR INIT COLSPOT IDXREF
JOB= DEFPIX INTEGRATE CORRECT
NAME_TEMPLATE_OF_DATA_FRAMES= custom_????.cbf
!LIB= /home/opid27/lib/dectris-neggia.so
SPACE_GROUP_NUMBER=12 ! 0 if unknown
UNIT_CELL_CONSTANTS=127.77 127.77 67.26 90.000 90.000 90.000
FRIEDEL'S_LAW=TRUE ! This acts only on the CORRECT step
FRACTION_OF_POLARIZATION=0.5
ROTATION_AXIS=0 1 0
POLARIZATION_PLANE_NORMAL=1 0 0
DIRECTION_OF_DETECTOR_X-AXIS=1 0 0
DIRECTION_OF_DETECTOR_Y-AXIS=0 1 0
INCIDENT_BEAM_DIRECTION=0 0 -1
OSCILLATION_RANGE= 0.5
OVERLOAD=999999
TRUSTED_REGION= 0.1 1.2
DATA_RANGE= 1 2
SPOT_RANGE= 2 3
BACKGROUND_RANGE= 1 1
NX= 2068 NY= 2162 QX= 0.075000 QY= 0.075000
DETECTOR= PILATUS
DETECTOR_DISTANCE= 151.8
ORGX= 1052 ORGY= 1102
X-RAY_WAVELENGTH=0.2846
UNTRUSTED_RECTANGLE= 1 2 3 4
SENSOR_THICKNESS=1 !mm
SILICON= 20 !1/mm
MINIMUM_NUMBER_OF_PIXELS_IN_A_SPOT=5 ! default of 6 is sometimes too high
MAXIMUM_NUMBER_OF_STRONG_PIXELS=999 ! total number of strong pixels used for indexation
BACKGROUND_PIXEL=1.5 ! used by COLSPOT and INTEGRATE
SIGNAL_PIXEL=4.5 ! needs to be lager than BACKGROUND_PIXEL, specifies standard deviation, used in COLSPOT and INTEGRATE
REFINE(IDXREF)= AXIS
REFINE(INTEGRATE)= BEAM POSITION
REFINE(CORRECT)= CELL
EXCLUDE_RESOLUTION_RANGE= 3.93 3.87 !ice-ring at 3.897 Angstrom
INCLUDE_RESOLUTION_RANGE=60 2.9 ! after CORRECT, insert high resol limit; re-run CORRECT
BEAM_DIVERGENCE= 0.17577 BEAM_DIVERGENCE_E.S.D.= 0.01758
REFLECTING_RANGE= 1.87829 REFLECTING_RANGE_E.S.D.= 0.26833
TEST= CUSTOM
"""
)
[docs]
def test_create_xds_inp_requires_directory_output(tmp_path: PathLike):
output_file = tmp_path / "not_a_dir"
output_file.write_text("x", encoding="ascii")
with pytest.raises(RuntimeError, match="CreateXDSInp") as excinfo:
task = CreateXDSInp(inputs=_task_inputs(output_file))
task.execute()
assert isinstance(excinfo.value.__cause__, NotADirectoryError)
[docs]
def test_create_xds_inp_accepts_legacy_aliases(tmp_path: PathLike):
inputs = _task_inputs(tmp_path / "cbf")
inputs.pop("beam")
inputs.pop("distance")
inputs.pop("wavelength")
inputs["orgx"] = 1052
inputs["orgy"] = 1102
inputs["detector_distance"] = 151.8
inputs["wavelength"] = 0.2846
inputs["name_template_of_data_frames"] = "frame_?.cbf"
task = CreateXDSInp(inputs=inputs)
task.execute()
content = (tmp_path / "cbf" / "XDS.INP").read_text(encoding="ascii")
assert "ORGX= 1052 ORGY= 1102" in content
assert "DETECTOR_DISTANCE= 151.8" in content
assert "X-RAY_WAVELENGTH=0.2846" in content
[docs]
def test_create_xds_inp_includes_starting_values_when_provided(tmp_path: PathLike):
inputs = _task_inputs(tmp_path / "cbf" / "frame_{index}.cbf")
inputs["starting_angle"] = -36
inputs["starting_frame"] = 1
task = CreateXDSInp(inputs=inputs)
task.execute()
content = (tmp_path / "cbf" / "XDS.INP").read_text(encoding="ascii")
assert "STARTING_ANGLE= -36\nSTARTING_FRAME= 1\n" in content