Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/819.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow the creation of a simple ``Crystal`` model from PHIL parameters.
52 changes: 52 additions & 0 deletions src/dxtbx/model/crystal.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import iotbx.phil
from cctbx.sgtbx import space_group as SG
from scitbx import matrix

Expand All @@ -16,6 +17,25 @@
MosaicCrystalSauter2014,
)

crystal_phil_scope = iotbx.phil.parse(
"""
crystal
.expert_level = 1
.short_caption = "Crystal overrides"
{
unit_cell = None
.type = unit_cell

A_matrix = None
.type = floats(size=9)
.help = "The crystal setting A=UB matrix. If set, this will override the unit cell."

space_group = None
.type = space_group
}
"""
)


class CrystalFactory:
@staticmethod
Expand Down Expand Up @@ -126,3 +146,35 @@ def from_mosflm_matrix(
_c = rotate_mosflm_to_imgCIF * c

return Crystal(_a, _b, _c, space_group=space_group)

@staticmethod
def from_phil(
params: iotbx.phil.scope_extract,
reference: Crystal | None = None,
) -> Crystal:
"""
Convert the phil parameters into a crystal model
"""

all_params = [
params.crystal.unit_cell,
params.crystal.A_matrix,
params.crystal.space_group,
]
if all_params.count(None) == len(all_params):
return None

if reference is None:
crystal = Crystal((1, 0, 0), (0, 1, 0), (0, 0, 1), "P1")
else:
crystal = reference
crystal.reset_scan_points()

if params.crystal.unit_cell is not None:
crystal.set_unit_cell(params.crystal.unit_cell)
if params.crystal.A_matrix is not None:
crystal.set_A(params.crystal.A_matrix)
if params.crystal.space_group is not None:
crystal.set_space_group(params.crystal.space_group.group())

return crystal
41 changes: 41 additions & 0 deletions tests/model/test_crystal_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from cctbx import crystal, sgtbx, uctbx
from cctbx.sgtbx import change_of_basis_op
from iotbx.phil import parse
from libtbx.test_utils import approx_equal
from scitbx import matrix
from scitbx.array_family import flex
Expand All @@ -18,6 +19,7 @@
MosaicCrystalKabsch2010,
MosaicCrystalSauter2014,
)
from dxtbx.model.crystal import crystal_phil_scope

from .crystal_model_old import crystal_model_old

Expand Down Expand Up @@ -639,3 +641,42 @@ def test_recalculated_cell(crystal_class):
xl.set_recalculated_unit_cell(uctbx.unit_cell((11, 12, 13, 90, 90, 90)))
assert xl.get_recalculated_cell_parameter_sd() == ()
assert xl.get_recalculated_cell_volume_sd() == 0


def test_from_phil():
params = crystal_phil_scope.extract()
assert CrystalFactory.from_phil(params) is None

params = crystal_phil_scope.fetch(
parse(
"""
crystal {
unit_cell = 10, 20, 30, 90, 90, 90
space_group = P222
}
"""
)
).extract()

# Create the crystal
crystal = CrystalFactory.from_phil(params)
assert crystal.get_unit_cell().parameters() == (10, 20, 30, 90, 90, 90)
assert crystal.get_space_group().type().lookup_symbol() == "P 2 2 2"

params = crystal_phil_scope.fetch(
parse(
"""
crystal {
A_matrix = 0.0541, 0.0832, -0.0060, 0.0049, -0.0175, -0.0492, -0.0840, 0.0526, -0.0068
space_group = P4
}
"""
)
).extract()

# Create the crystal
crystal = CrystalFactory.from_phil(params)
assert crystal.get_A() == pytest.approx(
(0.0541, 0.0832, -0.0060, 0.0049, -0.0175, -0.0492, -0.0840, 0.0526, -0.0068)
)
assert crystal.get_space_group().type().lookup_symbol() == "P 4"