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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
from __future__ import annotations
import os
import shutil
import subprocess
import pytest
from dxtbx.serialize import load
from dials.array_family import flex
from dials.command_line.damage_analysis import PychefRunner, phil_scope, run
def test_damage_analysis_on_scaled_data(dials_data, run_in_tmp_path):
location = dials_data("l_cysteine_4_sweeps_scaled", pathlib=True)
refls = str(location / "scaled_20_25.refl")
expts = str(location / "scaled_20_25.expt")
args = [
str(refls),
str(expts),
"min_completeness=0.4",
"-v",
"json=dials.damage_analysis.json",
]
run(args)
assert run_in_tmp_path.joinpath("dials.damage_analysis.html").is_file()
assert run_in_tmp_path.joinpath("dials.damage_analysis.json").is_file()
def test_damage_analysis_damage_series(dials_data, run_in_tmp_path):
location = dials_data("l_cysteine_4_sweeps_scaled", pathlib=True)
refls = location / "scaled_20_25.refl"
expts = location / "scaled_20_25.expt"
args = [
str(refls),
str(expts),
"min_completeness=0.4",
"-v",
"json=dials.damage_analysis.json",
"dose_group_size=500",
"output.damage_series=True",
"output.accumulation_series=True",
]
run(args)
assert os.path.isfile("dials.damage_analysis.html")
assert os.path.isfile("dials.damage_analysis.json")
expected_series = [
"0_500",
"500_1000",
"1000_1500",
"1500_2000",
"0_1000",
"0_1500",
"0_2000",
]
for e in expected_series:
assert os.path.isfile(f"damage_series_{e}.refl")
assert os.path.isfile(f"damage_series_{e}.expt")
def test_setup_from_dials_data(dials_data):
"""Test dials.damage_analysis on scaled data."""
location = dials_data("l_cysteine_4_sweeps_scaled", pathlib=True)
refls = location / "scaled_20_25.refl"
expts = location / "scaled_20_25.expt"
table = flex.reflection_table.from_file(refls)
experiments = load.experiment_list(expts, check_format=False)
params = phil_scope.extract()
params.dose.experiments.shared_crystal = True
params.dose.experiments.dose_per_image = [1.0, 2.0]
# First experiment is images 1>1800, second 1>1700 (i.e. dose spans 1>5200)
runner = PychefRunner.from_dials_data_files(params, experiments, table)
assert max(runner.dose) == 5198 # last reflection measured not quite at end
assert min(runner.dose) == 2
# Now try again in 'standard' mode i.e. not shared crystal, and set a
# starting dose
params.dose.experiments.shared_crystal = False
params.dose.experiments.dose_per_image = [1.0]
params.dose.experiments.starting_doses = [10, 10]
runner = PychefRunner.from_dials_data_files(params, experiments, table)
assert max(runner.dose) == 1800 + 10
assert min(runner.dose) == 2 + 10
def test_damage_analysis_on_scaled_mtz(dials_data, run_in_tmp_path):
location = dials_data("l_cysteine_4_sweeps_scaled", pathlib=True)
refls = str(location / "scaled_20_25.refl")
expts = str(location / "scaled_20_25.expt")
# First export the data
command = [shutil.which("dials.export"), refls, expts]
result = subprocess.run(command, cwd=run_in_tmp_path, capture_output=True)
assert not result.returncode and not result.stderr
assert os.path.isfile("scaled.mtz")
args = [
str(run_in_tmp_path / "scaled.mtz"),
"anomalous=True",
"json=dials.damage_analysis.json",
]
run(args)
assert run_in_tmp_path.joinpath("dials.damage_analysis.html").is_file()
assert run_in_tmp_path.joinpath("dials.damage_analysis.json").is_file()
def test_damage_analysis_mtz_damage_series(dials_data, run_in_tmp_path):
"""Test dials.damage_analysis on scaled data."""
location = dials_data("l_cysteine_4_sweeps_scaled", pathlib=True)
refls = location / "scaled_20_25.refl"
expts = location / "scaled_20_25.expt"
# First export the data
command = [shutil.which("dials.export"), refls, expts]
result = subprocess.run(command, cwd=run_in_tmp_path, capture_output=True)
assert not result.returncode and not result.stderr
assert os.path.isfile("scaled.mtz")
args = [
str(run_in_tmp_path / "scaled.mtz"),
"anomalous=True",
"json=dials.damage_analysis.json",
"dose_group_size=500",
"output.damage_series=True",
"output.accumulation_series=True",
]
run(args)
assert os.path.isfile("dials.damage_analysis.html")
assert os.path.isfile("dials.damage_analysis.json")
expected_series = [
"0_500",
"500_1000",
"1000_1500",
"1500_2000",
"0_1000",
"0_1500",
"0_2000",
]
for e in expected_series:
assert os.path.isfile(f"damage_series_{e}.mtz")
def test_damage_analysis_input_handling(dials_data, run_in_tmp_path):
"""Test that errors are handled if more than one refl file, no refl/expt
file or unscaled data."""
location = dials_data("l_cysteine_4_sweeps_scaled", pathlib=True)
refls = str(location / "scaled_20_25.refl")
expts = str(location / "scaled_20_25.expt")
# Too many refl files
args = [refls, expts, refls]
with pytest.raises(SystemExit):
run(args)
# No refl file
args = [expts]
with pytest.raises(SystemExit):
run(args)
# No expt file
args = [refls]
with pytest.raises(SystemExit):
run(args)
def test_damage_analysis_fails_on_unscaled_data(dials_data, run_in_tmp_path):
location = dials_data("l_cysteine_dials_output", pathlib=True)
refls = str(location / "20_integrated.pickle")
expts = str(location / "20_integrated_experiments.json")
args = [refls, expts]
with pytest.raises(SystemExit):
run(args)
|