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
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ Now compile and install sire:

A small word of warning, the compilation can easily take over an hour!

If `ccache <https://ccache.dev>`__ is on your ``PATH``, then ``setup.py`` will
use it automatically, which makes rebuilds much faster when developing. It
isn't included in the pixi environments, so install it separately if needed.

Other pixi environments are available depending on your needs:

* ``pixi install -e default`` - core sire dependencies only
Expand Down
7 changes: 5 additions & 2 deletions corelib/src/libs/SireVol/triclinicbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ QDataStream &operator>>(QDataStream &ds, TriclinicBox &box)
else if (v == 2)
{
ds >> box.v0 >> box.v1 >> box.v2 >> box.rotation_matrix >> box.cell_matrix >> box.cell_matrix_inverse >> box.dist_max >> box._alpha >> box._beta >> box._gamma >> box.vol >> box.is_rotated >> box.is_reduced >> box.invlength;

// M and max_length aren't streamed, so recompute them.
box.setAttributes();
}
else
throw version_error(v, "1,2", r_box, CODELOC);
Expand Down Expand Up @@ -269,7 +272,6 @@ void TriclinicBox::rotate(double precision)
this->v1.setZ(0);
}


// Now set the box attributes.
this->setAttributes();
}
Expand Down Expand Up @@ -405,6 +407,7 @@ TriclinicBox &TriclinicBox::operator=(const TriclinicBox &other)
rotation_matrix = other.rotation_matrix;
cell_matrix = other.cell_matrix;
cell_matrix_inverse = other.cell_matrix_inverse;
M = other.M;
dist_max = other.dist_max;
max_length = other.max_length;
_alpha = other._alpha;
Expand Down Expand Up @@ -571,7 +574,7 @@ SireUnits::Dimension::Length TriclinicBox::maximumCutoff() const
if (this->isReduced())
{
QList<double> diagonals = {this->v0.x(), this->v1.y(), this->v2.z()};
return SireUnits::Dimension::Length(*std::min_element(diagonals.begin(), diagonals.end())/2.0);
return SireUnits::Dimension::Length(*std::min_element(diagonals.begin(), diagonals.end()) / 2.0);
}
// Otherwise, use half the norm of the smallest box vector.
else
Expand Down
4 changes: 4 additions & 0 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ organisation on `GitHub <https://github.com/openbiosim/sire>`__.
bindings. Structures are now exchanged with the gemmi Python module using gemmi's
own binary serialization, removing the pybind11 dependency.

* Fixed ``TriclinicBox`` losing its metric matrix when streamed or assigned, which gave
wrong minimum image distances from ``calc_dist``, e.g. for trajectory frame spaces. This
broke ``sire.restraints.boresch_search()`` for triclinic boxes.

`2026.2.0 <https://github.com/openbiosim/sire/compare/2026.1.0...2026.2.0>`__ - September 2026
----------------------------------------------------------------------------------------------

Expand Down
37 changes: 37 additions & 0 deletions tests/vol/test_triclinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,43 @@ def test_stream():
assert recovered_box == box


def test_stream_calc_dist():
"""
Test that a streamed TriclinicBox gives the same minimum image distance
as the original.
"""

tmp_dir = tempfile.TemporaryDirectory()
s3_file = f"{tmp_dir.name}/box.s3"

box = sr.vol.TriclinicBox.truncated_octahedron(50.0, True, True)

sr.stream.save(box, s3_file)
recovered_box = sr.stream.load(s3_file)

p0 = sr.maths.Vector(1, 2, 3)
p1 = sr.maths.Vector(7, -2, 5)
dist = sr.maths.Vector.distance(p0, p1).value()

assert box.calc_dist(p0, p1) == pytest.approx(dist)
assert recovered_box.calc_dist(p0, p1) == pytest.approx(dist)


def test_default_calc_dist():
"""
Test that a default constructed TriclinicBox gives Cartesian distances.
"""

box = sr.vol.TriclinicBox()

p0 = sr.maths.Vector(1, 2, 3)
p1 = sr.maths.Vector(7, -2, 5)

assert box.calc_dist(p0, p1) == pytest.approx(
sr.maths.Vector.distance(p0, p1).value()
)


def test_max_cutoff(ala_mols):
"""
Test that the maximum cutoff is set correctly.
Expand Down