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
58 changes: 58 additions & 0 deletions corelib/src/libs/SireIO/biosimspace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,64 @@ namespace SireIO
return ion;
}

System setCoordinates(System &system, const QVector<QVector<float>> &coordinates, const bool is_lambda1, const PropertyMap &map)
{
// Make sure that the number of coordinates matches the number of atoms.
if (system.nAtoms() != coordinates.size())
{
throw SireError::incompatible_error(
QObject::tr("Number of coordinates (%1) does not match number of atoms in the system (%2)!")
.arg(coordinates.size())
.arg(system.nAtoms()),
CODELOC);
}

// Keep track of the current coordinate index.
unsigned coord_idx = 0;

// Loop over all molecules in the system in MolIdx order.
for (int i = 0; i < system.nMolecules(); ++i)
{
// Extract the molecule and make it editable.
auto molecule = system.molecule(MolIdx(i)).molecule().edit();

QString prop_name = "coordinates";
if (molecule.hasProperty("is_perturbable"))
{
if (is_lambda1)
prop_name = "coordinates1";
else
prop_name = "coordinates0";
}

// Get the coordinate property.
const auto coord_prop = map[prop_name];

// Loop over all atoms in the molecule.
for (int j = 0; j < molecule.nAtoms(); ++j)
{
// Construct the new coordinate.
const auto coord = Vector(coordinates[coord_idx + j][0],
coordinates[coord_idx + j][1],
coordinates[coord_idx + j][2]);

// Set the new coordinates.
molecule = molecule.edit()
.atom(AtomIdx(j))
.setProperty(coord_prop, coord)
.molecule();
}

// Update the molecule in the system.
system.update(molecule.commit());

// Update the coordinate index.
coord_idx += molecule.nAtoms();
}

return system;
}

Vector cross(const Vector &v0, const Vector &v1)
{
double nx = v0.y() * v1.z() - v0.z() * v1.y();
Expand Down
21 changes: 21 additions & 0 deletions corelib/src/libs/SireIO/biosimspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,26 @@ namespace SireIO
SIREIO_EXPORT Molecule createChlorineIon(
const Vector &coords, const QString model, const PropertyMap &map = PropertyMap());

//! Set the coordinates of the entire system.
/* \param system
The molecular system of interest.

\param coordinates
The new coordinates for the system.

\param is_lambda1
Whether this is for the lambda = 1 state.

\param map
A dictionary of user-defined molecular property names.

\retval system
The system with updated coordinates.
*/
SIREIO_EXPORT SireSystem::System setCoordinates(
SireSystem::System &system, const QVector<QVector<float>> &coordinates,
const bool is_lambda1 = false, const PropertyMap &map = PropertyMap());

Vector cross(const Vector &v0, const Vector &v1);
} // namespace SireIO

Expand All @@ -366,6 +386,7 @@ SIRE_EXPOSE_FUNCTION(SireIO::updateAndPreserveOrder)
SIRE_EXPOSE_FUNCTION(SireIO::updateCoordinatesAndVelocities)
SIRE_EXPOSE_FUNCTION(SireIO::createSodiumIon)
SIRE_EXPOSE_FUNCTION(SireIO::createChlorineIon)
SIRE_EXPOSE_FUNCTION(SireIO::setCoordinates)

SIRE_END_HEADER

Expand Down
2 changes: 2 additions & 0 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ organisation on `GitHub <https://github.com/openbiosim/sire>`__.
* Add missing dihedrals for all 1-4 atom paths in ``Sire::MM::AmberParams::validateAndFix``,
not just the first one found.

* Added :func:`Sire::IO::setCoordinates` function to set atom coordinates of an entire system.

`2025.2.0 <https://github.com/openbiosim/sire/compare/2025.1.0...2025.2.0>`__ - October 2025
--------------------------------------------------------------------------------------------

Expand Down
84 changes: 84 additions & 0 deletions tests/biosimspace/test_set_coordinates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import sire as sr
import numpy as np


def test_set_coordinates(ala_mols):

# Clone the input molecules.
mols = ala_mols.clone()

# Store the existing coordinates as a NumPy array.
coords = sr.io.get_coords_array(mols)

# Modify the system to multiply all coordinates by 2.
new_mols = sr.legacy.IO.setCoordinates(mols._system, (coords * 2.0).tolist())

# Get the new coordinates as a NumPy array.
new_coords = sr.io.get_coords_array(new_mols)

# Make sure the new coordinates are as expected.
assert (
np.sum(np.round(new_coords / coords)) == 6.0 * mols.num_atoms()
), "Coordinates were not set correctly."


def test_set_coordinates_perturbable(merged_ethane_methanol):

# Clone the input molecules.
mols = merged_ethane_methanol.clone()

# First link to the reference state.
mols = sr.morph.link_to_reference(mols)

# Store the existing coordinates as a NumPy array.
coords = sr.io.get_coords_array(mols)

# Modify the system to multiply all coordinates by 2.
new_mols = sr.legacy.IO.setCoordinates(mols._system, (coords * 2.0).tolist())

# Link to the reference state.
new_mols = sr.system.System(new_mols)
new_mols = sr.morph.link_to_reference(new_mols)

# Get the new coordinates as a NumPy array.
new_coords = sr.io.get_coords_array(new_mols)

# Divide the new coordinates by the old coordinates.
ratio = new_coords / coords

# Set any NaN values to 2.0 (in case of zero coordinates).
ratio = np.where(np.isnan(ratio), 2.0, ratio)

# Make sure the new coordinates are as expected.
assert (
np.sum(np.round(ratio)) == 6.0 * mols.num_atoms()
), "Coordinates were not set correctly."

# Now link to the perturbable state.
mols = sr.morph.link_to_perturbed(mols)

# Store the existing coordinates as a NumPy array.
coords = sr.io.get_coords_array(mols)

# Modify the system to multiply all coordinates by 2.
new_mols = sr.legacy.IO.setCoordinates(
mols._system, (coords * 2.0).tolist(), is_lambda1=True
)

# Link to the perturbable state.
new_mols = sr.system.System(new_mols)
new_mols = sr.morph.link_to_perturbed(new_mols)

# Get the new coordinates as a NumPy array.
new_coords = sr.io.get_coords_array(new_mols)

# Divide the new coordinates by the old coordinates.
ratio = new_coords / coords

# Set any NaN values to 2.0 (in case of zero coordinates).
ratio = np.where(np.isnan(ratio), 2.0, ratio)

# Make sure the new coordinates are as expected.
assert (
np.sum(np.round(ratio)) == 6.0 * mols.num_atoms()
), "Coordinates were not set correctly."
10 changes: 6 additions & 4 deletions wrapper/IO/Amber.pypp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ namespace bp = boost::python;

SireIO::Amber __copy__(const SireIO::Amber &other){ return SireIO::Amber(other); }

#include "Helpers/copy.hpp"

#include "Qt/qdatastream.hpp"

const char* pvt_get_name(const SireIO::Amber&){ return "SireIO::Amber";}
Expand Down Expand Up @@ -197,13 +199,13 @@ void register_Amber_class(){
"writeCrd"
, writeCrd_function_value
, ( bp::arg("mols"), bp::arg("space"), bp::arg("crdfile"), bp::arg("map")=SireBase::PropertyMap() )
, "" );
, "Write the coordinates of the molecules in the passed MoleculeGroup in the\npassed Space to an Amber7,\nformat coordinaterestart file. The passed property map is used to find\nthe required properties" );

}
Amber_exposer.staticmethod( "typeName" );
Amber_exposer.def( "__copy__", &__copy__);
Amber_exposer.def( "__deepcopy__", &__copy__);
Amber_exposer.def( "clone", &__copy__);
Amber_exposer.def( "__copy__", &__copy__<SireIO::Amber>);
Amber_exposer.def( "__deepcopy__", &__copy__<SireIO::Amber>);
Amber_exposer.def( "clone", &__copy__<SireIO::Amber>);
Amber_exposer.def( "__rlshift__", &__rlshift__QDataStream< ::SireIO::Amber >,
bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() );
Amber_exposer.def( "__rrshift__", &__rrshift__QDataStream< ::SireIO::Amber >,
Expand Down
27 changes: 24 additions & 3 deletions wrapper/IO/AmberPrm.pypp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ namespace bp = boost::python;

#include "SireBase/parallel.h"

#include "SireBase/progressbar.h"

#include "SireBase/propertylist.h"

#include "SireBase/stringproperty.h"

#include "SireBase/tempdir.h"
Expand All @@ -37,6 +41,8 @@ namespace bp = boost::python;

#include "SireMM/internalff.h"

#include "SireMM/lj1264parameter.h"

#include "SireMM/ljparameter.h"

#include "SireMaths/maths.h"
Expand Down Expand Up @@ -125,6 +131,8 @@ namespace bp = boost::python;

SireIO::AmberPrm __copy__(const SireIO::AmberPrm &other){ return SireIO::AmberPrm(other); }

#include "Helpers/copy.hpp"

#include "Qt/qdatastream.hpp"

#include "Helpers/str.hpp"
Expand All @@ -142,6 +150,7 @@ void register_AmberPrm_class(){
.value("INTEGER", SireIO::AmberPrm::INTEGER)
.value("FLOAT", SireIO::AmberPrm::FLOAT)
.value("STRING", SireIO::AmberPrm::STRING)
.value("FFLOAT", SireIO::AmberPrm::FFLOAT)
.export_values()
;
AmberPrm_exposer.def( bp::init< QString const &, bp::optional< SireBase::PropertyMap const & > >(( bp::arg("filename"), bp::arg("map")=SireBase::PropertyMap() ), "Construct by reading from the file called filename") );
Expand Down Expand Up @@ -603,6 +612,18 @@ void register_AmberPrm_class(){
, bp::release_gil_policy()
, "" );

}
{ //::SireIO::AmberPrm::warnings

typedef ::QStringList ( ::SireIO::AmberPrm::*warnings_function_type)( ) const;
warnings_function_type warnings_function_value( &::SireIO::AmberPrm::warnings );

AmberPrm_exposer.def(
"warnings"
, warnings_function_value
, bp::release_gil_policy()
, "" );

}
{ //::SireIO::AmberPrm::what

Expand All @@ -618,9 +639,9 @@ void register_AmberPrm_class(){
}
AmberPrm_exposer.staticmethod( "parse" );
AmberPrm_exposer.staticmethod( "typeName" );
AmberPrm_exposer.def( "__copy__", &__copy__);
AmberPrm_exposer.def( "__deepcopy__", &__copy__);
AmberPrm_exposer.def( "clone", &__copy__);
AmberPrm_exposer.def( "__copy__", &__copy__<SireIO::AmberPrm>);
AmberPrm_exposer.def( "__deepcopy__", &__copy__<SireIO::AmberPrm>);
AmberPrm_exposer.def( "clone", &__copy__<SireIO::AmberPrm>);
AmberPrm_exposer.def( "__rlshift__", &__rlshift__QDataStream< ::SireIO::AmberPrm >,
bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() );
AmberPrm_exposer.def( "__rrshift__", &__rrshift__QDataStream< ::SireIO::AmberPrm >,
Expand Down
8 changes: 5 additions & 3 deletions wrapper/IO/AmberRst.pypp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ namespace bp = boost::python;

SireIO::AmberRst __copy__(const SireIO::AmberRst &other){ return SireIO::AmberRst(other); }

#include "Helpers/copy.hpp"

#include "Qt/qdatastream.hpp"

#include "Helpers/str.hpp"
Expand Down Expand Up @@ -338,9 +340,9 @@ void register_AmberRst_class(){
}
AmberRst_exposer.staticmethod( "parse" );
AmberRst_exposer.staticmethod( "typeName" );
AmberRst_exposer.def( "__copy__", &__copy__);
AmberRst_exposer.def( "__deepcopy__", &__copy__);
AmberRst_exposer.def( "clone", &__copy__);
AmberRst_exposer.def( "__copy__", &__copy__<SireIO::AmberRst>);
AmberRst_exposer.def( "__deepcopy__", &__copy__<SireIO::AmberRst>);
AmberRst_exposer.def( "clone", &__copy__<SireIO::AmberRst>);
AmberRst_exposer.def( "__rlshift__", &__rlshift__QDataStream< ::SireIO::AmberRst >,
bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() );
AmberRst_exposer.def( "__rrshift__", &__rrshift__QDataStream< ::SireIO::AmberRst >,
Expand Down
8 changes: 5 additions & 3 deletions wrapper/IO/AmberRst7.pypp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ namespace bp = boost::python;

SireIO::AmberRst7 __copy__(const SireIO::AmberRst7 &other){ return SireIO::AmberRst7(other); }

#include "Helpers/copy.hpp"

#include "Qt/qdatastream.hpp"

#include "Helpers/str.hpp"
Expand Down Expand Up @@ -345,9 +347,9 @@ void register_AmberRst7_class(){
}
AmberRst7_exposer.staticmethod( "parse" );
AmberRst7_exposer.staticmethod( "typeName" );
AmberRst7_exposer.def( "__copy__", &__copy__);
AmberRst7_exposer.def( "__deepcopy__", &__copy__);
AmberRst7_exposer.def( "clone", &__copy__);
AmberRst7_exposer.def( "__copy__", &__copy__<SireIO::AmberRst7>);
AmberRst7_exposer.def( "__deepcopy__", &__copy__<SireIO::AmberRst7>);
AmberRst7_exposer.def( "clone", &__copy__<SireIO::AmberRst7>);
AmberRst7_exposer.def( "__rlshift__", &__rlshift__QDataStream< ::SireIO::AmberRst7 >,
bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() );
AmberRst7_exposer.def( "__rrshift__", &__rrshift__QDataStream< ::SireIO::AmberRst7 >,
Expand Down
8 changes: 5 additions & 3 deletions wrapper/IO/AmberTraj.pypp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ namespace bp = boost::python;

SireIO::AmberTraj __copy__(const SireIO::AmberTraj &other){ return SireIO::AmberTraj(other); }

#include "Helpers/copy.hpp"

#include "Qt/qdatastream.hpp"

#include "Helpers/str.hpp"
Expand Down Expand Up @@ -322,9 +324,9 @@ void register_AmberTraj_class(){
}
AmberTraj_exposer.staticmethod( "parse" );
AmberTraj_exposer.staticmethod( "typeName" );
AmberTraj_exposer.def( "__copy__", &__copy__);
AmberTraj_exposer.def( "__deepcopy__", &__copy__);
AmberTraj_exposer.def( "clone", &__copy__);
AmberTraj_exposer.def( "__copy__", &__copy__<SireIO::AmberTraj>);
AmberTraj_exposer.def( "__deepcopy__", &__copy__<SireIO::AmberTraj>);
AmberTraj_exposer.def( "clone", &__copy__<SireIO::AmberTraj>);
AmberTraj_exposer.def( "__rlshift__", &__rlshift__QDataStream< ::SireIO::AmberTraj >,
bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() );
AmberTraj_exposer.def( "__rrshift__", &__rrshift__QDataStream< ::SireIO::AmberTraj >,
Expand Down
8 changes: 5 additions & 3 deletions wrapper/IO/BrokenParser.pypp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ namespace bp = boost::python;

SireIO::BrokenParser __copy__(const SireIO::BrokenParser &other){ return SireIO::BrokenParser(other); }

#include "Helpers/copy.hpp"

#include "Qt/qdatastream.hpp"

#include "Helpers/str.hpp"
Expand Down Expand Up @@ -260,9 +262,9 @@ void register_BrokenParser_class(){

}
BrokenParser_exposer.staticmethod( "typeName" );
BrokenParser_exposer.def( "__copy__", &__copy__);
BrokenParser_exposer.def( "__deepcopy__", &__copy__);
BrokenParser_exposer.def( "clone", &__copy__);
BrokenParser_exposer.def( "__copy__", &__copy__<SireIO::BrokenParser>);
BrokenParser_exposer.def( "__deepcopy__", &__copy__<SireIO::BrokenParser>);
BrokenParser_exposer.def( "clone", &__copy__<SireIO::BrokenParser>);
BrokenParser_exposer.def( "__rlshift__", &__rlshift__QDataStream< ::SireIO::BrokenParser >,
bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() );
BrokenParser_exposer.def( "__rrshift__", &__rrshift__QDataStream< ::SireIO::BrokenParser >,
Expand Down
Loading