diff --git a/corelib/src/libs/SireCAS/lambdaschedule.cpp b/corelib/src/libs/SireCAS/lambdaschedule.cpp index bd3ccfa3c..ee254de35 100644 --- a/corelib/src/libs/SireCAS/lambdaschedule.cpp +++ b/corelib/src/libs/SireCAS/lambdaschedule.cpp @@ -39,18 +39,40 @@ using namespace SireCAS; using namespace SireBase; using namespace SireStream; +QString _get_lever_name(QString force, QString lever) +{ + force = force.trimmed().simplified().replace(" ", "_").replace(":", "."); + lever = lever.trimmed().simplified().replace(" ", "_").replace(":", "."); + + return force + "::" + lever; +} + +QString _fix_lever_name(const QString &lever) +{ + if (lever.contains("::")) + { + return lever; + } + else + { + return "*::" + lever; + } +} + static RegisterMetaType r_schedule; QDataStream &operator<<(QDataStream &ds, const LambdaSchedule &schedule) { - writeHeader(ds, r_schedule, 1); + writeHeader(ds, r_schedule, 3); SharedDataStream sds(ds); sds << schedule.constant_values + << schedule.force_names << schedule.lever_names << schedule.stage_names << schedule.default_equations << schedule.stage_equations + << schedule.mol_schedules << static_cast(schedule); return ds; @@ -67,14 +89,44 @@ QDataStream &operator>>(QDataStream &ds, LambdaSchedule &schedule) { VersionID v = readHeader(ds, r_schedule); - if (v == 1) + if (v == 1 or v == 2 or v == 3) { SharedDataStream sds(ds); - sds >> schedule.constant_values >> - schedule.lever_names >> schedule.stage_names >> - schedule.default_equations >> schedule.stage_equations >> - static_cast(schedule); + sds >> schedule.constant_values; + + if (v == 3) + sds >> schedule.force_names; + + sds >> schedule.lever_names >> schedule.stage_names >> + schedule.default_equations >> schedule.stage_equations; + + if (v == 2 or v == 3) + sds >> schedule.mol_schedules; + + if (v < 3) + { + // need to make sure that the lever names are namespaced + auto fixed_lever_names = QStringList(); + + for (auto &lever : schedule.lever_names) + { + fixed_lever_names.append(_fix_lever_name(lever)); + + for (auto &stage_equations : schedule.stage_equations) + { + if (stage_equations.contains(lever)) + { + auto fixed_lever = _fix_lever_name(lever); + stage_equations[fixed_lever] = stage_equations.take(lever); + } + } + } + + schedule.lever_names = fixed_lever_names; + } + + sds >> static_cast(schedule); for (auto &expression : schedule.default_equations) { @@ -92,7 +144,7 @@ QDataStream &operator>>(QDataStream &ds, LambdaSchedule &schedule) } } else - throw version_error(v, "1", r_schedule, CODELOC); + throw version_error(v, "1, 2, 3", r_schedule, CODELOC); return ds; } @@ -103,7 +155,9 @@ LambdaSchedule::LambdaSchedule() : ConcreteProperty() LambdaSchedule::LambdaSchedule(const LambdaSchedule &other) : ConcreteProperty(other), + mol_schedules(other.mol_schedules), constant_values(other.constant_values), + force_names(other.force_names), lever_names(other.lever_names), stage_names(other.stage_names), default_equations(other.default_equations), stage_equations(other.stage_equations) @@ -118,7 +172,9 @@ LambdaSchedule &LambdaSchedule::operator=(const LambdaSchedule &other) { if (this != &other) { + mol_schedules = other.mol_schedules; constant_values = other.constant_values; + force_names = other.force_names; lever_names = other.lever_names; stage_names = other.stage_names; default_equations = other.default_equations; @@ -131,7 +187,9 @@ LambdaSchedule &LambdaSchedule::operator=(const LambdaSchedule &other) bool LambdaSchedule::operator==(const LambdaSchedule &other) const { - return constant_values == other.constant_values and + return mol_schedules == other.mol_schedules and + force_names == other.force_names and + constant_values == other.constant_values and lever_names == other.lever_names and stage_names == other.stage_names and default_equations == other.default_equations and @@ -177,10 +235,13 @@ QString LambdaSchedule::toString() const .arg(this->stage_names[i]) .arg(this->default_equations[i].toOpenMMString())); - for (const auto &lever : this->stage_equations[i].keys()) + auto keys = this->stage_equations[i].keys(); + std::sort(keys.begin(), keys.end()); + + for (auto lever : keys) { lines.append(QString(" %1: %2") - .arg(lever) + .arg(lever.replace("*::", "")) .arg(this->stage_equations[i][lever].toOpenMMString())); } } @@ -192,6 +253,18 @@ QString LambdaSchedule::toString() const .arg(this->constant_values[constant])); } + if (not this->mol_schedules.isEmpty()) + { + lines.append(" Molecule schedules:"); + + for (const auto &mol_id : this->mol_schedules.keys()) + { + lines.append(QString(" %1: %2") + .arg(mol_id) + .arg(this->mol_schedules[mol_id].toString())); + } + } + return QObject::tr("LambdaSchedule(\n%1\n)") .arg(lines.join("\n")); } @@ -225,6 +298,23 @@ LambdaSchedule LambdaSchedule::charge_scaled_morph(double scale) return l; } +LambdaSchedule LambdaSchedule::standard_decouple(bool perturbed_is_decoupled) +{ + LambdaSchedule l; + l.addDecoupleStage(perturbed_is_decoupled); + + return l; +} + +LambdaSchedule LambdaSchedule::charge_scaled_decouple(double scale, bool perturbed_is_decoupled) +{ + LambdaSchedule l; + l.addDecoupleStage(perturbed_is_decoupled); + l.addChargeScaleStages(scale); + + return l; +} + /** Return the symbol used to represent the :lambda: coordinate. * This symbol is used to represent the per-stage :lambda: * variable that goes from 0.0-1.0 within that stage. @@ -297,7 +387,7 @@ SireCAS::Symbol LambdaSchedule::getConstantSymbol(const QString &constant) const */ void LambdaSchedule::addLever(const QString &lever) { - if (this->lever_names.contains(lever)) + if (lever == "*" or this->lever_names.contains(lever)) return; this->lever_names.append(lever); @@ -312,7 +402,7 @@ void LambdaSchedule::addLevers(const QStringList &levers) { for (const auto &lever : levers) { - if (not this->lever_names.contains(lever)) + if (not(lever == "*" or this->lever_names.contains(lever))) this->lever_names.append(lever); } } @@ -365,6 +455,79 @@ QStringList LambdaSchedule::getLevers() const return this->lever_names; } +/** Add a force to a schedule. This is only useful if you want to + * plot how the equations would affect the lever. Forces will be + * automatically added by any perturbation run that needs them, + * so you don't need to add them manually yourself. + */ +void LambdaSchedule::addForce(const QString &force) +{ + if (force == "*" or this->force_names.contains(force)) + return; + + this->force_names.append(force); +} + +/** Add some forces to a schedule. This is only useful if you want to + * plot how the equations would affect the lever. Forces will be + * automatically added by any perturbation run that needs them, + * so you don't need to add them manually yourself. + */ +void LambdaSchedule::addForces(const QStringList &forces) +{ + for (const auto &force : forces) + { + if (not(force == "*" or this->force_names.contains(force))) + this->force_names.append(force); + } +} + +/** Remove a force from a schedule. This will not impact any + * perturbation runs that use this schedule, as any missing + * forces will be re-added. + */ +void LambdaSchedule::removeForce(const QString &force) +{ + if (not this->force_names.contains(force)) + return; + + int idx = this->force_names.indexOf(force); + + this->force_names.removeAt(idx); +} + +/** Remove some forces from a schedule. This will not impact any + * perturbation runs that use this schedule, as any missing + * forces will be re-added. + */ +void LambdaSchedule::removeForces(const QStringList &forces) +{ + for (const auto &force : forces) + { + this->removeForce(force); + } +} + +/** Return the number of forces that have been explicitly added + * to the schedule. Note that forces will be automatically added + * by any perturbation run that needs them, so you don't normally + * need to manage them manually yourself. + */ +int LambdaSchedule::nForces() const +{ + return this->force_names.count(); +} + +/** Return all of the forces that have been explicitly added + * to the schedule. Note that forces will be automatically added + * by any perturbation run that needs them, so you don't normally + * need to manage them manually yourself. + */ +QStringList LambdaSchedule::getForces() const +{ + return this->force_names; +} + /** Return the number of stages in this schedule */ int LambdaSchedule::nStages() const { @@ -468,6 +631,18 @@ void LambdaSchedule::addMorphStage() this->addMorphStage("morph"); } +void LambdaSchedule::addDecoupleStage(bool perturbed_is_decoupled) +{ + this->addDecoupleStage("decouple", perturbed_is_decoupled); +} + +void LambdaSchedule::addDecoupleStage(const QString &name, bool perturbed_is_decoupled) +{ + throw SireError::incomplete_code(QObject::tr( + "Decouple stages are not yet implemented."), + CODELOC); +} + /** Sandwich the current set of stages with a charge-descaling and * a charge-scaling stage. This prepends a charge-descaling stage * that scales the charge parameter down from `initial` to @@ -485,16 +660,16 @@ void LambdaSchedule::addChargeScaleStages(const QString &decharge_name, // make sure all of the existing stages for the charge lever are scaled for (int i = 0; i < this->stage_names.count(); ++i) { - this->setEquation(this->stage_names[i], "charge", - scl * this->stage_equations[i].value("charge", this->default_equations[i])); + this->setEquation(this->stage_names[i], "*", "charge", + scale * this->stage_equations[i].value("charge", this->default_equations[i])); } // now prepend the decharging stage, and append the recharging stage this->prependStage(decharge_name, this->initial()); this->appendStage(recharge_name, this->final()); - this->setEquation(decharge_name, "charge", (1.0 - ((1.0 - scl) * this->lam())) * this->initial()); - this->setEquation(recharge_name, "charge", (1.0 - ((1.0 - scl) * (1.0 - this->lam()))) * this->final()); + this->setEquation(decharge_name, "*", "charge", (1.0 - ((1.0 - scl) * this->lam())) * this->initial()); + this->setEquation(recharge_name, "*", "charge", (1.0 - ((1.0 - scl) * (1.0 - this->lam()))) * this->final()); } /** Sandwich the current set of stages with a charge-descaling and @@ -518,6 +693,11 @@ void LambdaSchedule::addChargeScaleStages(double scale) void LambdaSchedule::prependStage(const QString &name, const SireCAS::Expression &equation) { + if (name == "*") + throw SireError::invalid_key(QObject::tr( + "The stage name '*' is reserved and cannot be used."), + CODELOC); + auto e = equation; if (e == default_morph_equation) @@ -548,7 +728,12 @@ void LambdaSchedule::prependStage(const QString &name, void LambdaSchedule::appendStage(const QString &name, const SireCAS::Expression &equation) { - if (this->stage_names.contains(name)) + if (name == "*") + throw SireError::invalid_key(QObject::tr( + "The stage name '*' is reserved and cannot be used."), + CODELOC); + + else if (this->stage_names.contains(name)) throw SireError::invalid_key(QObject::tr( "Cannot append the stage %1 as it already exists.") .arg(name), @@ -573,6 +758,11 @@ void LambdaSchedule::insertStage(int i, const QString &name, const SireCAS::Expression &equation) { + if (name == "*") + throw SireError::invalid_key(QObject::tr( + "The stage name '*' is reserved and cannot be used."), + CODELOC); + auto e = equation; if (e == default_morph_equation) @@ -600,6 +790,19 @@ void LambdaSchedule::insertStage(int i, this->stage_equations.insert(i, QHash()); } +/** Remove the stage 'stage' */ +void LambdaSchedule::removeStage(const QString &stage) +{ + if (not this->stage_names.contains(stage)) + return; + + int idx = this->stage_names.indexOf(stage); + + this->stage_names.removeAt(idx); + this->default_equations.removeAt(idx); + this->stage_equations.removeAt(idx); +} + /** Append a stage called 'name' which uses the passed 'equation' * to the end of this schedule. The equation will be the default * equation that scales all parameters (levers) that don't have @@ -608,6 +811,11 @@ void LambdaSchedule::insertStage(int i, void LambdaSchedule::addStage(const QString &name, const Expression &equation) { + if (name == "*") + throw SireError::invalid_key(QObject::tr( + "The stage name '*' is reserved and cannot be used."), + CODELOC); + this->appendStage(name, equation); } @@ -634,8 +842,8 @@ int LambdaSchedule::find_stage(const QString &stage) const * to control any levers in this stage that don't have * their own custom equation. */ -void LambdaSchedule::setDefaultEquation(const QString &stage, - const Expression &equation) +void LambdaSchedule::setDefaultStageEquation(const QString &stage, + const Expression &equation) { auto e = equation; @@ -645,15 +853,27 @@ void LambdaSchedule::setDefaultEquation(const QString &stage, this->default_equations[this->find_stage(stage)] = e; } -/** Set the custom equation used to control the specified - * `lever` at the stage `stage` to `equation`. This equation - * will only be used to control the parameters for the - * specified lever at the specified stage. +/** Set the custom equation used to control the specified 'lever' + * for the specified 'force' at the stage 'stage' to 'equation'. + * This equation will only be used to control the parameters for the + * specified lever in the specified force at the specified stage */ void LambdaSchedule::setEquation(const QString &stage, + const QString &force, const QString &lever, - const Expression &equation) + const SireCAS::Expression &equation) { + if (stage == "*") + { + // we do this for all stages + for (int i = 0; i < this->nStages(); ++i) + { + this->setEquation(this->stage_names[i], force, lever, equation); + } + + return; + } + auto e = equation; if (e == default_morph_equation) @@ -661,53 +881,190 @@ void LambdaSchedule::setEquation(const QString &stage, auto &lever_expressions = this->stage_equations[this->find_stage(stage)]; - if (not this->lever_names.contains(lever)) + if (lever != "*" and not this->lever_names.contains(lever)) this->addLever(lever); - lever_expressions[lever] = e; + if (force != "*" and not this->force_names.contains(force)) + this->addForce(force); + + lever_expressions[_get_lever_name(force, lever)] = e; } -/** Remove the custom equation for the specified `lever` at the - * specified `stage`. The lever will now use the default - * equation at this stage. +/** Remove the custom equation for the specified `lever` in the + * specified 'force' at the specified `stage`. + * The lever will now use the equation specified for this + * lever for this stage, or the default lever for the stage + * if this isn't set */ void LambdaSchedule::removeEquation(const QString &stage, + const QString &force, const QString &lever) { - if (not(this->lever_names.contains(lever) and this->stage_names.contains(stage))) + if (stage == "*") + { + // remove from all stages + for (int i = 0; i < this->nStages(); ++i) + { + this->removeEquation(this->stage_names[i], force, lever); + } + return; + } int idx = this->stage_names.indexOf(stage); - this->stage_equations[idx].remove(lever); + this->stage_equations[idx].remove(_get_lever_name(force, lever)); } -/** Return the default equation used to control the parameters for - * the stage `stage`. +/** Return whether or not the specified 'lever' in the specified 'force' + * at the specified 'stage' has a custom equation set for it */ -Expression LambdaSchedule::getEquation(const QString &stage) const +bool LambdaSchedule::hasForceSpecificEquation(const QString &stage, + const QString &force, + const QString &lever) const { - const int idx = this->find_stage(stage); + if (stage == "*") + throw SireError::invalid_key(QObject::tr( + "The stage name '*' is reserved and cannot be used " + "when querying for force-specific equations."), + CODELOC); - return this->default_equations[idx]; + int idx = this->stage_names.indexOf(stage); + + if (idx < 0) + throw SireError::invalid_key(QObject::tr( + "There is no stage name called '%1'. Valid stages are %2.") + .arg(stage) + .arg(this->stage_names.join(", ")), + CODELOC); + + if (force == "*") + return false; + else + return this->stage_equations[idx].contains(_get_lever_name(force, lever)); +} + +SireCAS::Expression LambdaSchedule::_getEquation(int stage, + const QString &force, + const QString &lever) const +{ + if (stage < 0 or stage >= this->nStages()) + throw SireError::invalid_key(QObject::tr( + "There is no stage number %1. Valid stages are 0-%2.") + .arg(stage) + .arg(this->nStages() - 1), + CODELOC); + + const auto default_lever = _get_lever_name("*", lever); + + if (force == "*") + { + return this->stage_equations[stage].value( + default_lever, this->default_equations[stage]); + } + else + { + return this->stage_equations[stage].value( + _get_lever_name(force, lever), + this->stage_equations[stage].value( + default_lever, + this->default_equations[stage])); + } } -/** Return the equation used to control the specified `lever` - * at the specified `stage`. This will be a custom equation - * if that has been set for this lever, or else the - * default equation for this stage. +/** Return the equation used to control the specified 'lever' + * in the specified 'force' at the specified 'stage'. This will + * be a custom equation if that has been set for this lever in this + * force, or else it will be a custom equation set for this lever, + * else it will be the default equation for this stage */ Expression LambdaSchedule::getEquation(const QString &stage, + const QString &force, const QString &lever) const { - if (not this->lever_names.contains(lever)) - return this->getEquation(stage); + if (stage == "*") + throw SireError::invalid_key(QObject::tr( + "The stage name '*' is reserved and cannot be used " + "when getting individual equations."), + CODELOC); - const int idx = this->find_stage(stage); + int idx = this->stage_names.indexOf(stage); + + if (idx < 0) + throw SireError::invalid_key(QObject::tr( + "There is no stage name called '%1'. Valid stages are %2.") + .arg(stage) + .arg(this->stage_names.join(", ")), + CODELOC); - const auto &lever_expressions = this->stage_equations[idx]; + return _getEquation(idx, force, lever); +} - return lever_expressions.value(lever, this->default_equations[idx]); +/** Set 'schedule' as the molecule-specific schedule for the + * perturbable molecule (or part of molecule) that is identified by the + * passed 'pert_mol_id'. This schedule will be used to control + * all of the levers for this molecule (or part of molecule), + * and replaces any levers provided by this schedule + */ +void LambdaSchedule::setMoleculeSchedule(int pert_mol_id, + const LambdaSchedule &schedule) +{ + this->mol_schedules.insert(pert_mol_id, schedule); + this->mol_schedules[pert_mol_id].mol_schedules.clear(); +} + +/** Return whether or not the perturbable molecule (or part of molecule) + * that is identified by passed 'pert_mol_id' has its own schedule */ +bool LambdaSchedule::hasMoleculeSchedule(int pert_mol_id) const +{ + return this->mol_schedules.contains(pert_mol_id); +} + +/** Remove the perturbable molecule-specific schedule associated + * with the perturbable molecule (or part of molecule) that is + * identified by the passed 'pert_mol_id'. + */ +void LambdaSchedule::removeMoleculeSchedule(int pert_mol_id) +{ + this->mol_schedules.remove(pert_mol_id); +} + +/** Remove the perturbable molecule-specific schedule associated + * with the perturbable molecule (or part of molecule) that is + * identified by the passed 'pert_mol_id'. This returns the + * schedule that was removed. If no such schedule exists, then + * a copy of this schedule is returned. + */ +LambdaSchedule LambdaSchedule::takeMoleculeSchedule(int pert_mol_id) +{ + if (this->mol_schedules.contains(pert_mol_id)) + { + return this->mol_schedules.take(pert_mol_id); + } + else + { + auto ret = *this; + ret.mol_schedules.clear(); + return ret; + } +} + +/** Return the schedule used to control perturbations for the + * perturbable molecule (or part of molecule) that is identified by the + * passed 'pert_mol_id'. This schedule will be used to control + * all of the levers for this molecule (or part of molecule). + * + * This returns this schedule if there is no specified schedule + * for this molecule + */ +const LambdaSchedule &LambdaSchedule::getMoleculeSchedule(int pert_mol_id) const +{ + auto it = this->mol_schedules.constFind(pert_mol_id); + + if (it == this->mol_schedules.constEnd()) + return *this; + else + return it.value(); } QVector generate_lambdas(int num_values) @@ -736,8 +1093,8 @@ QVector generate_lambdas(int num_values) return lambda_values; } -/** Return the list of lever stages that are used for the passed list - * of lambda values. The lever names will be returned in the matching +/** Return the list of stages that are used for the passed list + * of lambda values. The stage names will be returned in the matching * order of the lambda values. */ QStringList LambdaSchedule::getLeverStages(const QVector &lambda_values) const @@ -762,7 +1119,7 @@ QStringList LambdaSchedule::getLeverStages(const QVector &lambda_values) return stages; } -/** Return the lever stages used for the list of `nvalue` lambda values +/** Return the stages used for the list of `nvalue` lambda values * generated for the global lambda value between 0 and 1 inclusive. */ QStringList LambdaSchedule::getLeverStages(int nvalues) const @@ -770,9 +1127,9 @@ QStringList LambdaSchedule::getLeverStages(int nvalues) const return this->getLeverStages(generate_lambdas(nvalues)); } -/** Return the lever name and parameter values for that lever +/** Return the stage name and parameter values for that lever * for the specified list of lambda values, assuming that a - * parameter for that lever has an initial value of + * parameter for that stage has an initial value of * `initial_value` and a final value of `final_value`. This * is mostly useful for testing and graphing how this * schedule would change some hyperthetical forcefield @@ -847,6 +1204,7 @@ QHash> LambdaSchedule::getLeverValues( } /** Return the parameters for the specified lever called `lever_name` + * in the force 'force' * that have been morphed from the passed list of initial values * (in `initial`) to the passed list of final values (in `final`) * for the specified global value of :lambda: (in `lambda_value`). @@ -856,7 +1214,8 @@ QHash> LambdaSchedule::getLeverValues( * * This morphs a single floating point parameters. */ -double LambdaSchedule::morph(const QString &lever_name, +double LambdaSchedule::morph(const QString &force, + const QString &lever, double initial, double final, double lambda_value) const { @@ -867,19 +1226,17 @@ double LambdaSchedule::morph(const QString &lever_name, const auto resolved = this->resolve_lambda(lambda_value); const int stage = std::get<0>(resolved); - const auto equation = this->stage_equations[stage].value( - lever_name, this->default_equations[stage]); - Values input_values = this->constant_values; input_values.set(this->lam(), std::get<1>(resolved)); input_values.set(this->initial(), initial); input_values.set(this->final(), final); - return equation(input_values); + return this->_getEquation(stage, force, lever)(input_values); } /** Return the parameters for the specified lever called `lever_name` + * in the specified force, * that have been morphed from the passed list of initial values * (in `initial`) to the passed list of final values (in `final`) * for the specified global value of :lambda: (in `lambda_value`). @@ -891,7 +1248,8 @@ double LambdaSchedule::morph(const QString &lever_name, * of this function that morphs integer parameters, in which * case the result would be rounded to the nearest integer. */ -QVector LambdaSchedule::morph(const QString &lever_name, +QVector LambdaSchedule::morph(const QString &force, + const QString &lever, const QVector &initial, const QVector &final, double lambda_value) const @@ -902,7 +1260,7 @@ QVector LambdaSchedule::morph(const QString &lever_name, throw SireError::incompatible_error(QObject::tr( "The number of initial and final parameters for lever %1 is not the same. " "%2 versus %3. They need to be the same.") - .arg(lever_name) + .arg(lever) .arg(initial.count()) .arg(final.count()), CODELOC); @@ -914,8 +1272,7 @@ QVector LambdaSchedule::morph(const QString &lever_name, const auto resolved = this->resolve_lambda(lambda_value); const int stage = std::get<0>(resolved); - const auto equation = this->stage_equations[stage].value( - lever_name, this->default_equations[stage]); + const auto equation = this->_getEquation(stage, force, lever); QVector morphed(nparams); auto morphed_data = morphed.data(); @@ -948,6 +1305,7 @@ QVector LambdaSchedule::morph(const QString &lever_name, } /** Return the parameters for the specified lever called `lever_name` + * for the specified 'force' * that have been morphed from the passed list of initial values * (in `initial`) to the passed list of final values (in `final`) * for the specified global value of :lambda: (in `lambda_value`). @@ -958,7 +1316,8 @@ QVector LambdaSchedule::morph(const QString &lever_name, * This function morphs integer parameters. In this case, * the result will be the rounded to the nearest integer. */ -QVector LambdaSchedule::morph(const QString &lever_name, +QVector LambdaSchedule::morph(const QString &force, + const QString &lever, const QVector &initial, const QVector &final, double lambda_value) const @@ -969,7 +1328,7 @@ QVector LambdaSchedule::morph(const QString &lever_name, throw SireError::incompatible_error(QObject::tr( "The number of initial and final parameters for lever %1 is not the same. " "%2 versus %3. They need to be the same.") - .arg(lever_name) + .arg(lever) .arg(initial.count()) .arg(final.count()), CODELOC); @@ -981,11 +1340,7 @@ QVector LambdaSchedule::morph(const QString &lever_name, const auto resolved = this->resolve_lambda(lambda_value); const int stage = std::get<0>(resolved); - const auto equation = this->stage_equations[stage].value( - lever_name, this->default_equations[stage]); - - Values input_values = this->constant_values; - input_values.set(this->lam(), std::get<1>(resolved)); + const auto equation = this->_getEquation(stage, force, lever); QVector morphed(nparams); @@ -993,14 +1348,26 @@ QVector LambdaSchedule::morph(const QString &lever_name, const auto initial_data = initial.constData(); const auto final_data = final.constData(); - for (int i = 0; i < nparams; ++i) + if (equation == default_morph_equation) + { + for (int i = 0; i < nparams; ++i) + { + morphed_data[i] = int((1.0 - lambda_value) * initial_data[i] + + lambda_value * final_data[i]); + } + } + else { - input_values.set(this->initial(), double(initial_data[i])); - input_values.set(this->final(), double(final_data[i])); + Values input_values = this->constant_values; + input_values.set(this->lam(), std::get<1>(resolved)); + + for (int i = 0; i < nparams; ++i) + { + input_values.set(this->initial(), double(initial_data[i])); + input_values.set(this->final(), double(final_data[i])); - // the result is the resulting float rounded to the nearest - // integer - morphed_data[i] = int(std::floor(equation(input_values) + 0.5)); + morphed_data[i] = int(equation(input_values)); + } } return morphed; diff --git a/corelib/src/libs/SireCAS/lambdaschedule.h b/corelib/src/libs/SireCAS/lambdaschedule.h index 412d0b8c5..7e48e8e01 100644 --- a/corelib/src/libs/SireCAS/lambdaschedule.h +++ b/corelib/src/libs/SireCAS/lambdaschedule.h @@ -79,6 +79,9 @@ namespace SireCAS static LambdaSchedule standard_morph(); static LambdaSchedule charge_scaled_morph(double scale = 0.2); + static LambdaSchedule standard_decouple(bool perturbed_is_decoupled = true); + static LambdaSchedule charge_scaled_decouple(double scale = 0.2, bool perturbed_is_decoupled = true); + static SireCAS::Symbol lam(); static SireCAS::Symbol initial(); static SireCAS::Symbol final(); @@ -95,6 +98,16 @@ namespace SireCAS QStringList getLevers() const; + void addForce(const QString &force); + void addForces(const QStringList &forces); + + void removeForce(const QString &force); + void removeForces(const QStringList &forces); + + int nForces() const; + + QStringList getForces() const; + int nStages() const; QStringList getStages() const; @@ -116,6 +129,8 @@ namespace SireCAS const QString &stage, const SireCAS::Expression &equation); + void removeStage(const QString &stage); + void addMorphStage(); void addMorphStage(const QString &name); @@ -124,20 +139,39 @@ namespace SireCAS const QString &recharge_name, double scale = 0.2); - void setEquation(const QString &stage, - const QString &lever, - const SireCAS::Expression &equation); + void addDecoupleStage(bool perturbed_is_decoupled = true); + void addDecoupleStage(const QString &name, bool perturbed_is_decoupled = true); + + void setDefaultStageEquation(const QString &stage, + const SireCAS::Expression &equation); - void setDefaultEquation(const QString &stage, - const SireCAS::Expression &equation); + void setEquation(const QString &stage = "*", + const QString &force = "*", + const QString &lever = "*", + const SireCAS::Expression &equation = SireCAS::Expression()); - void removeEquation(const QString &stage, - const QString &lever); + void removeEquation(const QString &stage = "*", + const QString &force = "*", + const QString &lever = "*"); - SireCAS::Expression getEquation(const QString &stage) const; + bool hasForceSpecificEquation(const QString &stage = "*", + const QString &force = "*", + const QString &lever = "*") const; - SireCAS::Expression getEquation(const QString &stage, - const QString &lever) const; + SireCAS::Expression getEquation(const QString &stage = "*", + const QString &force = "*", + const QString &lever = "*") const; + + void setMoleculeSchedule(int pert_mol_id, + const LambdaSchedule &schedule); + + bool hasMoleculeSchedule(int pert_mol_id) const; + + void removeMoleculeSchedule(int pert_mol_id); + + LambdaSchedule takeMoleculeSchedule(int pert_mol_id); + + const LambdaSchedule &getMoleculeSchedule(int pert_mol_id) const; QHash> getLeverValues(const QVector &lambda_values, double initial = 1.0, @@ -160,18 +194,20 @@ namespace SireCAS SireCAS::Symbol getConstantSymbol(const QString &constant) const; - double morph(const QString &lever, - double initial, double final, double lambda_value) const; + double morph(const QString &force = "*", const QString &lever = "*", + double initial = 0, double final = 1, double lambda_value = 0) const; - QVector morph(const QString &lever, - const QVector &initial, - const QVector &final, - double lambda_value) const; + QVector morph(const QString &force = "*", + const QString &lever = "*", + const QVector &initial = QVector(), + const QVector &final = QVector(), + double lambda_value = 0.0) const; - QVector morph(const QString &lever, - const QVector &initial, - const QVector &final, - double lambda_value) const; + QVector morph(const QString &force = "*", + const QString &lever = "*", + const QVector &initial = QVector(), + const QVector &final = QVector(), + double lambda_value = 0.0) const; double clamp(double lambda_value) const; @@ -180,9 +216,19 @@ namespace SireCAS std::tuple resolve_lambda(double lambda) const; + SireCAS::Expression _getEquation(int stage, const QString &force, const QString &lever) const; + + /** Additional schedules for extra molecules, i.e. that + * run in parallel alongside the default schedule + */ + QHash mol_schedules; + /** The set of all constants used across all stages */ SireCAS::Values constant_values; + /** The names of all of the forces */ + QStringList force_names; + /** The names of all of the levers provided by the forcefields */ QStringList lever_names; diff --git a/corelib/src/libs/SireIO/amberprm.cpp b/corelib/src/libs/SireIO/amberprm.cpp index 980c226ea..fe84f638f 100644 --- a/corelib/src/libs/SireIO/amberprm.cpp +++ b/corelib/src/libs/SireIO/amberprm.cpp @@ -562,7 +562,12 @@ void AmberPrm::rebuildLJParameters() bool is_exception = false; - if (std::abs(lj_ij.epsilon().value() - expect.epsilon().value()) <= 1e-6) + if (std::abs(epsilon) < 1e-6 and std::abs(expect.epsilon().value()) < 1e-6) + { + // this is a LJ pair that involves a ghost or dummy atom + // It should not impact exceptions or combining rules + } + else if (std::abs(lj_ij.epsilon().value() - expect.epsilon().value()) <= 1e-6) { if (std::abs(lj_ij.sigma().value() - expect.sigma().value()) > 1e-6) { diff --git a/corelib/src/libs/SireMol/moleculedata.cpp b/corelib/src/libs/SireMol/moleculedata.cpp index 3d28a19ff..d60133099 100644 --- a/corelib/src/libs/SireMol/moleculedata.cpp +++ b/corelib/src/libs/SireMol/moleculedata.cpp @@ -363,7 +363,8 @@ bool MoleculeData::operator!=(const MoleculeData &other) const /** Return a new MoleculeData that contains only the passed selected atoms. This allows parts of the molecule to be pulled out and used independently */ -MoleculeData MoleculeData::extract(const AtomSelection &selected_atoms) const +MoleculeData MoleculeData::extract(const AtomSelection &selected_atoms, + bool to_same_molecule) const { selected_atoms.assertCompatibleWith(*this); @@ -433,7 +434,8 @@ MoleculeData MoleculeData::extract(const AtomSelection &selected_atoms) const // renumber the molecule to remove confusion as to why // extracted molecules cannot be combined - editor.renumber(); + if (not to_same_molecule) + editor.renumber(); return editor.commit().data(); } diff --git a/corelib/src/libs/SireMol/moleculedata.h b/corelib/src/libs/SireMol/moleculedata.h index e35e031a0..b1cc92cb7 100644 --- a/corelib/src/libs/SireMol/moleculedata.h +++ b/corelib/src/libs/SireMol/moleculedata.h @@ -188,7 +188,8 @@ namespace SireMol return props; } - MoleculeData extract(const AtomSelection &selected_atoms) const; + MoleculeData extract(const AtomSelection &selected_atoms, + bool to_same_molecule = false) const; QStringList propertyKeys() const; diff --git a/corelib/src/libs/SireMol/moleculeview.cpp b/corelib/src/libs/SireMol/moleculeview.cpp index d988b6cc8..fb9747855 100644 --- a/corelib/src/libs/SireMol/moleculeview.cpp +++ b/corelib/src/libs/SireMol/moleculeview.cpp @@ -476,20 +476,6 @@ void MoleculeView::update(const MoleculeData &moldata) { if (moldata.number() == this->data().number()) { - if (moldata.info().UID() != this->data().info().UID()) - { - throw SireError::incompatible_error(QObject::tr( - "Cannot update molecule %1 because the layout for the new " - "version of the molecule (%2) is different. This is likely " - "because the molecule has been edited and the layout of " - "atoms, residues etc has been changed. To update this molecule " - "you will need to replace it in the container.") - .arg(this->molecule().toString()) - .arg(Molecule(moldata).toString()), - CODELOC); - } - - // this is the same molecule with the same molecular layout d = moldata; } } @@ -1934,7 +1920,7 @@ Selector MoleculeView::selectAllSegments() const selected atoms. This allows the used to pull out parts of a larger molecule, e.g. if they want to have only selected residues in a protein and do not want to have to store or manipulate the larger protein molecule */ -Molecule MoleculeView::extract() const +Molecule MoleculeView::extract(bool to_same_molecule) const { if (d->info().nAtoms() == 0 or this->isEmpty()) return Molecule(); @@ -1944,7 +1930,7 @@ Molecule MoleculeView::extract() const else { - return Molecule(d->extract(this->selection())); + return Molecule(d->extract(this->selection(), to_same_molecule)); } } diff --git a/corelib/src/libs/SireMol/moleculeview.h b/corelib/src/libs/SireMol/moleculeview.h index f7223a99a..1ac86e47c 100644 --- a/corelib/src/libs/SireMol/moleculeview.h +++ b/corelib/src/libs/SireMol/moleculeview.h @@ -278,7 +278,7 @@ namespace SireMol virtual bool isSelector() const; - Molecule extract() const; + Molecule extract(bool to_same_molecule = false) const; void update(const MoleculeData &moldata); void update(const MoleculeView &molview); diff --git a/corelib/src/libs/SireMol/selectorm.hpp b/corelib/src/libs/SireMol/selectorm.hpp index 1372c4553..0859fc9c7 100644 --- a/corelib/src/libs/SireMol/selectorm.hpp +++ b/corelib/src/libs/SireMol/selectorm.hpp @@ -65,7 +65,7 @@ namespace SireMol { friend SIREMOL_EXPORT QDataStream & ::operator<< <>(QDataStream &, const SelectorM &); - friend SIREMOL_EXPORT QDataStream & ::operator>><>(QDataStream &, SelectorM &); + friend SIREMOL_EXPORT QDataStream & ::operator>> <>(QDataStream &, SelectorM &); public: typedef typename QList>::const_iterator iterator; @@ -93,7 +93,7 @@ namespace SireMol template SelectorM(const SelectorM &other, const QList &idxs); template - SelectorM(const SelectorM &other, const QString &name); + SelectorM(const SelectorM &other, const QString &name, const SireBase::PropertyMap &map = SireBase::PropertyMap()); template SelectorM(const SelectorM &other, const typename T::ID &id); @@ -1072,7 +1072,9 @@ namespace SireMol template template - SIRE_OUTOFLINE_TEMPLATE SelectorM::SelectorM(const SelectorM &other, const QString &name) + SIRE_OUTOFLINE_TEMPLATE SelectorM::SelectorM(const SelectorM &other, + const QString &name, + const SireBase::PropertyMap &map) : SireBase::ConcreteProperty, SireBase::Property>() { for (const auto &view : other) @@ -1093,7 +1095,7 @@ namespace SireMol // try a search try { - this->operator=(SelectorM(other.search(name))); + this->operator=(SelectorM(other.search(name, map))); } catch (...) { diff --git a/corelib/src/libs/SireMove/openmmfrenergyst.cpp b/corelib/src/libs/SireMove/openmmfrenergyst.cpp index 48109223d..17481a97f 100644 --- a/corelib/src/libs/SireMove/openmmfrenergyst.cpp +++ b/corelib/src/libs/SireMove/openmmfrenergyst.cpp @@ -523,7 +523,7 @@ void OpenMMFrEnergyST::initialise() energybase = "" "(1.0 - isSolvent1 * isSolvent2 * SPOnOff) * (Hcs + Hls);" - "Hcs = (lambda^n) * 138.935456 * q_prod/sqrt(diff_cl+r^2);" + "Hcs = (lambda^n) * 138.9354558466661 * q_prod/sqrt(diff_cl+r^2);" "diff_cl = (1.0-lambda) * 0.01;" "Hls = 4.0 * eps_avg * (LJ*LJ-LJ);" "LJ=((sigma_avg * sigma_avg)/soft)^3;" @@ -571,7 +571,7 @@ void OpenMMFrEnergyST::initialise() intra_14_todummy = "" "Hcs + Hls;" - "Hcs=(lamtd^ntd)*138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=(lamtd^ntd)*138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamtd)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -599,7 +599,7 @@ void OpenMMFrEnergyST::initialise() intra_14_fromdummy = "" "Hcs + Hls;" - "Hcs=(lamfd^nfd)*138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=(lamfd^nfd)*138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamfd)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -629,7 +629,7 @@ void OpenMMFrEnergyST::initialise() intra_14_fromdummy_todummy = "" "Hcs + Hls;" - "Hcs=(lamFTD^nftd)*138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=(lamFTD^nftd)*138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamFTD)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -665,7 +665,7 @@ void OpenMMFrEnergyST::initialise() energybase = "" "(1.0 - isSolvent1 * isSolvent2 * SPOnOff) * (Hcs + Hls);" - "Hcs = 138.935456 * q_prod/sqrt(diff_cl+r^2);" + "Hcs = 138.9354558466661 * q_prod/sqrt(diff_cl+r^2);" "diff_cl = (1.0-lambda) * 0.01;" "Hls = 4.0 * eps_avg * (LJ*LJ-LJ);" "LJ=((sigma_avg * sigma_avg)/soft)^3;" @@ -713,7 +713,7 @@ void OpenMMFrEnergyST::initialise() intra_14_todummy = "" "Hcs + Hls;" - "Hcs=138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamtd)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -741,7 +741,7 @@ void OpenMMFrEnergyST::initialise() intra_14_fromdummy = "" "Hcs + Hls;" - "Hcs=(lamfd^nfd)*138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=(lamfd^nfd)*138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamfd)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -771,7 +771,7 @@ void OpenMMFrEnergyST::initialise() intra_14_fromdummy_todummy = "" "Hcs + Hls;" - "Hcs=138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamFTD)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -802,7 +802,7 @@ void OpenMMFrEnergyST::initialise() intra_14_clj = "" "Hl+Hc;" "Hl=4*eps_avg*((sigma_avg/r)^12-(sigma_avg/r)^6);" - "Hc=138.935456*q_prod/r;" + "Hc=138.9354558466661*q_prod/r;" "eps_avg = sqrt(lamhd*lamhd*eaend + (1-lamhd)*(1-lamhd)*eastart + lamhd*(1-lamhd)*emix);" "q_prod = lamhd*lamhd*qpend + (1-lamhd)*(1-lamhd)*qpstart + lamhd*(1-lamhd)*qmix;" ""; @@ -845,7 +845,7 @@ void OpenMMFrEnergyST::initialise() energybase = "" "(1.0 - isSolvent1 * isSolvent2 * SPOnOff) * (Hls + Hcs);" - "Hcs = (lambda^n) * 138.935456 * q_prod*(1/sqrt(diff_cl+r*r) + krflam*(diff_cl+r*r)-crflam);" + "Hcs = (lambda^n) * 138.9354558466661 * q_prod*(1/sqrt(diff_cl+r*r) + krflam*(diff_cl+r*r)-crflam);" "crflam = crf * src;" "krflam = krf * src * src * src;" "src = cutoff/sqrt(diff_cl + cutoff*cutoff);" @@ -912,7 +912,7 @@ void OpenMMFrEnergyST::initialise() intra_14_todummy = "" "withinCutoff*(Hcs + Hls);" "withinCutoff=step(cutofftd-r);" - "Hcs=(lamtd^ntd)*138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=(lamtd^ntd)*138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamtd)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -942,7 +942,7 @@ void OpenMMFrEnergyST::initialise() "" "withinCutoff*(Hcs + Hls);" "withinCutoff=step(cutofffd-r);" - "Hcs=(lamfd^nfd)*138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=(lamfd^nfd)*138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamfd)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -973,7 +973,7 @@ void OpenMMFrEnergyST::initialise() "" "withinCutoff*(Hcs + Hls);" "withinCutoff=step(cutoffftd-r);" - "Hcs=(lamFTD^nftd)*138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=(lamFTD^nftd)*138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamFTD)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -1012,7 +1012,7 @@ void OpenMMFrEnergyST::initialise() energybase = "" "(1.0 - isSolvent1 * isSolvent2 * SPOnOff) * (Hls + Hcs);" - "Hcs = 138.935456 * q_prod*(1/sqrt(diff_cl+r*r) + krflam*(diff_cl+r*r)-crflam);" + "Hcs = 138.9354558466661 * q_prod*(1/sqrt(diff_cl+r*r) + krflam*(diff_cl+r*r)-crflam);" "crflam = crf * src;" "krflam = krf * src * src * src;" "src = cutoff/sqrt(diff_cl + cutoff*cutoff);" @@ -1079,7 +1079,7 @@ void OpenMMFrEnergyST::initialise() intra_14_todummy = "" "withinCutoff*(Hcs + Hls);" "withinCutoff=step(cutofftd-r);" - "Hcs=138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamtd)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -1109,7 +1109,7 @@ void OpenMMFrEnergyST::initialise() "" "withinCutoff*(Hcs + Hls);" "withinCutoff=step(cutofffd-r);" - "Hcs=138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamfd)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -1141,7 +1141,7 @@ void OpenMMFrEnergyST::initialise() "" "withinCutoff*(Hcs + Hls);" "withinCutoff=step(cutoffftd-r);" - "Hcs=138.935456*q_prod/sqrt(diff_cl+r^2);" + "Hcs=138.9354558466661*q_prod/sqrt(diff_cl+r^2);" "diff_cl=(1.0-lamFTD)*0.01;" "Hls=4.0*eps_avg*(LJ*LJ-LJ);" "LJ=((sigma_avg*sigma_avg)/soft)^3;" @@ -1174,7 +1174,7 @@ void OpenMMFrEnergyST::initialise() "withinCutoff*(Hl+Hc);" "withinCutoff=step(cutoffhd-r);" "Hl=4*eps_avg*((sigma_avg/r)^12-(sigma_avg/r)^6);" - "Hc=138.935456*q_prod/r;" + "Hc=138.9354558466661*q_prod/r;" "eps_avg = sqrt(lamhd*lamhd*eaend + (1-lamhd)*(1-lamhd)*eastart + lamhd*(1-lamhd)*emix);" "q_prod = lamhd*lamhd*qpend + (1-lamhd)*(1-lamhd)*qpstart + lamhd*(1-lamhd)*qmix;" ""; @@ -1883,23 +1883,22 @@ void OpenMMFrEnergyST::initialise() restrainedAtoms.property(QString("Anchor(%1)").arg(i)).asA().toInt(); int atomnum = restrainedAtoms.property(QString("Atom(%1)").arg(i)).asA().toInt(); - double k = restrainedAtoms.property(QString("k(%1)").arg(i)).asA().toDouble(); - //double xref = restrainedAtoms.property(QString("x(%1)").arg(i)).asA().toDouble(); - //double yref = restrainedAtoms.property(QString("y(%1)").arg(i)).asA().toDouble(); - //double zref = restrainedAtoms.property(QString("z(%1)").arg(i)).asA().toDouble(); - //double k = restrainedAtoms.property(QString("k(%1)").arg(i)).asA().toDouble(); - //double d = restrainedAtoms.property(QString("d(%1)").arg(i)).asA().toDouble(); + double k = restrainedAtoms.property(QString("k(%1)").arg(i)).asA().toDouble(); + // double xref = restrainedAtoms.property(QString("x(%1)").arg(i)).asA().toDouble(); + // double yref = restrainedAtoms.property(QString("y(%1)").arg(i)).asA().toDouble(); + // double zref = restrainedAtoms.property(QString("z(%1)").arg(i)).asA().toDouble(); + // double k = restrainedAtoms.property(QString("k(%1)").arg(i)).asA().toDouble(); + // double d = restrainedAtoms.property(QString("d(%1)").arg(i)).asA().toDouble(); int atopenmmindex = AtomNumToOpenMMIndex[atomnum]; int anchoropenmmindex = AtomNumToOpenMMIndex[anchornum]; if (Debug) { - //qDebug() << "atomnum " << atomnum << " openmmindex " << openmmindex << " x " << xref << " y " - // << yref << " z " << zref << " k " << k << " d " << d; + // qDebug() << "atomnum " << atomnum << " openmmindex " << openmmindex << " x " << xref << " y " + // << yref << " z " << zref << " k " << k << " d " << d; qDebug() << "atomnum " << atomnum << " atopenmmindex " << atopenmmindex << " k " << k; qDebug() << "anchornum " << anchornum << " anchoropenmmindex " << anchoropenmmindex << " k " << k; - } // int posrestrdim = 5; @@ -1948,7 +1947,6 @@ void OpenMMFrEnergyST::initialise() QList dihedral_pert_list; QList dihedral_pert_swap_list; QList improper_pert_list; - QList improper_pert_swap_list; /* "Light" atoms are defined to have a mass of HMASS or smaller. This ensures that hydrogens in the HMR scheme will be constraint. The @@ -2468,7 +2466,7 @@ void OpenMMFrEnergyST::initialise() solute_torsion_perturbation = new OpenMM::CustomTorsionForce(openmm_str); solute_torsion_perturbation->addPerTorsionParameter("KJPerKcal"); - solute_torsion_perturbation_params[0] = 4.184; + solute_torsion_perturbation_params[0] = OpenMM::KJPerKcal; solute_torsion_perturbation->addGlobalParameter("lamdih", Alchemical_value); solute_torsion_perturbation->addTorsion(idx0, idx1, idx2, idx3, solute_torsion_perturbation_params); @@ -2482,11 +2480,9 @@ void OpenMMFrEnergyST::initialise() dihedral_pert_list.append(DihedralID(four.atom0(), four.atom1(), four.atom2(), four.atom3())); dihedral_pert_swap_list.append( - DihedralID(four.atom3(), four.atom1(), four.atom2(), four.atom0())); + DihedralID(four.atom3(), four.atom2(), four.atom1(), four.atom0())); improper_pert_list.append(ImproperID(four.atom0(), four.atom1(), four.atom2(), four.atom3())); - improper_pert_swap_list.append( - ImproperID(four.atom0(), four.atom1(), four.atom3(), four.atom2())); if (Debug) { @@ -2725,7 +2721,7 @@ void OpenMMFrEnergyST::initialise() if (solute.contains(molecule)) { // Solute molecule. Check if the current solute dihedral is in the perturbed improper list - if (improper_pert_list.indexOf(improper_ff) != -1 || improper_pert_swap_list.indexOf(improper_ff) != -1) + if (improper_pert_list.indexOf(improper_ff) != -1) { if (Debug) qDebug() << "Found Perturbed Improper\n"; diff --git a/corelib/src/libs/SireVol/triclinicbox.cpp b/corelib/src/libs/SireVol/triclinicbox.cpp index a5d45b045..fde411682 100644 --- a/corelib/src/libs/SireVol/triclinicbox.cpp +++ b/corelib/src/libs/SireVol/triclinicbox.cpp @@ -73,9 +73,7 @@ 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; + 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; } else throw version_error(v, "1,2", r_box, CODELOC); @@ -273,9 +271,9 @@ void TriclinicBox::reduce(double bias) */ // Perform the reduction. - this->v2 = this->v2 - this->v1*std::round(bias + this->v2.y() / this->v1.y()); - this->v2 = this->v2 - this->v0*std::round(bias + this->v2.x() / this->v0.x()); - this->v1 = this->v1 - this->v0*std::round(bias + this->v1.x() / this->v0.x()); + this->v2 = this->v2 - this->v1 * std::round(bias + this->v2.y() / this->v1.y()); + this->v2 = this->v2 - this->v0 * std::round(bias + this->v2.x() / this->v0.x()); + this->v1 = this->v1 - this->v0 * std::round(bias + this->v1.x() / this->v0.x()); // Now set the box attributes. this->setAttributes(); @@ -385,6 +383,7 @@ TriclinicBox &TriclinicBox::operator=(const TriclinicBox &other) v2 = other.v2; rotation_matrix = other.rotation_matrix; cell_matrix = other.cell_matrix; + cell_matrix_inverse = other.cell_matrix_inverse; dist_max = other.dist_max; max_length = other.max_length; _alpha = other._alpha; @@ -610,24 +609,25 @@ Vector TriclinicBox::wrapDelta(const Vector &v0, const Vector &v1) const // x if (frac_x >= 0.5) - int_x += 1.0; + int_x += 1; else if (frac_x <= -0.5) - int_x -= 1.0; + int_x -= 1; // y if (frac_y >= 0.5) - int_y += 1.0; + int_y += 1; else if (frac_y <= -0.5) - int_y -= 1.0; + int_y -= 1; // z if (frac_z >= 0.5) - int_z += 1.0; + int_z += 1; else if (frac_z <= -0.5) - int_z -= 1.0; + int_z -= 1; // Return the shifts over the box vectors. - return this->cell_matrix * Vector(int_x, int_y, int_z); + return this->cell_matrix * + Vector(int_x, int_y, int_z); } /** Calculate the distance between two points */ diff --git a/doc/source/api/convert.rst b/doc/source/api/convert.rst index 9cef6b272..ef17b5e50 100644 --- a/doc/source/api/convert.rst +++ b/doc/source/api/convert.rst @@ -6,3 +6,8 @@ Public API :members: :doc:`View Module Index ` + +.. automodule:: sire.convert.openmm + :members: + + :doc:`View Module Index ` diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index 81ebbe855..0f2712b32 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -15,6 +15,51 @@ organisation on `GitHub `__. `2024.1.0 `__ - March 2024 ------------------------------------------------------------------------------------------ +* BREAKING CHANGE: Updated the API of :class:`sire.cas.LambdaSchedule` so that + you have to use named arguments for many of the functions (e.g. + :meth:`~sire.cas.LambdaSchedule.set_equation`). This is because the addition + of force levers (as described below) made positional arguments ambiguous, + and we wanted to make the API more consistent. This is a breaking change, + +* Added the ability to customise the lambda schedule applied to a lambda lever + so that you can use different equations for different molecules and + different forces in the OpenMM context. This gives a lot of control over + how forcefield parameters are scaled with lambda. Specifically, this is used + to add support for calculating absolute binding free energies. + This is described in the new :doc:`tutorial chapter `. + +* Added "not-perturbable" constraints so that bonds and angles that change + with lambda are not perturbed. As part of this, have also added a + ``dynamic_constraints`` option that lets constrained bonds update with + lambda, so that they are set to the length corresponding to r0 at that + lambda value. Have also changed the constraints so that bonds will be + constrained to their r0 value, rather than their current length. + These constraints are ``X-not-perturbed``, meaning that it constrains + all ``X``, except for bonds or angles involving perturbed atoms. Or + ``X-not-heavy-perturbed``, meaning that it constrains all ``X``, except + for bonds or angles involving perturbed atoms, unless they involve a + hydrogen in any end state. The code to detect hydrogens has been improved, + now looking at mass, element and ambertype. There are options to control + this, described in the :doc:`OpenMM detailed guide `. + +* Added more automatic conversions, so that string will more readily auto-convert + to units where possible. Also added a ``sire.v`` function to make it easier to + create vectors of units, e.g. ``sire.v("1.0A", "2.0A", "3.0A")`` will create + a ``sire.maths.Vector(1, 2, 3)``, while ``sire.v([3, 4, 5], units="A ps-1")`` + will create a ``Velocity3D``. This is documented in the units cheat sheet. + +* You can now set the background color of a 3D view using the ``bgcolor="..."`` + keyword. This is documented in the view cheat sheet. + +* MacOS/ARM64 now includes AmberTools and Gromacs dependencies when built + for BioSimSpace (matching MacOS/X64 and Linux). + +* Updated the electrostatic softening potential to have an additional + ``shift_coulomb`` parameter, so that you can control how much the + distance is increased by the alpha softening parameter. This was + the equivalent of 10 Ã…, but has been set as default to 1 Ã… to match + the value used in somd. + * Added support for LJ 12-6-4 potentials, plus the ability to read and write LJ parameter exceptions to Amber topology files. This fixes issue #125. @@ -25,6 +70,35 @@ organisation on `GitHub `__. non-default values have been added, and also to set up a matrix which has a concept of unset values. +* Added a ``to_same_molecule`` argument to the ``mol.extract()`` function, + so that it is possible to keep the same molecule number for the extracted + molecule. As part of this, also relaxed the requirement that the + ``mol.update(...)`` function can only be called if the molecule layout + is not changed. You can now update even if you have changed the numbers + of atoms, residues etc. The ``to_same_molecule`` argument is default False, + so as not to change any existing behaviour. + +* Added lots of convenience functions to ``sire.morph``, as described in the + :doc:`new tutorial `. Functions include + linking to the reference or perturbed states for all molecules, or extracting + all of the reference or perturbed states of all molecules. Also I've added + functions for zeroing ghost torsions and creating molecules from pertfiles. + As part of this, I added an ``auto_commit`` argument to the + Perturbation ``link_to_reference`` and ``link_to_perturbed`` functions, + which defaults to True. This is a change in behaviour, but it makes the + API much easier to use. If you are affected by this, please let us know. + It was a little-used part of the code, with the main use case being the + replacement with the easier ``sire.morph.link_to_XXX`` functions. + +* Exposed the ``SOMMContext``, ``PerturbableOpenMMMolecule``, + ``OpenMMMetaData`` and ``LambdaLever`` classes to Python, as part of the + new ``sire.convert.openmm`` module. These are useful if you want more + control over OpenMM from Python. In particular, the ``PerturbableOpenMMMolecule`` + class lets you see all of the parameters that go into the OpenMM forces + that involve perturbable molecules. There are useful functions that can + be used to get changing parameters as dataframes, which is really useful + for debugging. These are described in the :doc:`new tutorial `. + * Added an ``AtomCoordMatcher`` to match atoms by coordinates in two selections. * Fix bug that disabled the ``DEBUG`` log level from the global logger. diff --git a/doc/source/cheatsheet/openmm.rst b/doc/source/cheatsheet/openmm.rst index 8443cf825..3c8e019f3 100644 --- a/doc/source/cheatsheet/openmm.rst +++ b/doc/source/cheatsheet/openmm.rst @@ -76,18 +76,43 @@ Available keys and allowable values are listed below. +------------------------------+----------------------------------------------------------+ | Key | Valid values | +==============================+==========================================================+ +| barostat_frequency | The frequency at which the barostat acts to perform | +| | the MC moves to change the box volume when performing | +| | constant pressure simulations (default 25). | ++------------------------------+----------------------------------------------------------+ +| check_for_h_by_ambertype | Boolean value, e.g. ``True`` or ``False`` as to whether | +| | hydrogen atoms can be detected based on a H? amber type. | +| | This is the default ``True``. | ++------------------------------+----------------------------------------------------------+ +| check_for_h_by_element | Boolean value, e.g. ``True`` or ``False`` as to whether | +| | hydrogen atoms can be detected based on the element. | +| | This is the default ``True``. | ++------------------------------+----------------------------------------------------------+ +| check_for_h_by_mass | Boolean value, e.g. ``True`` or ``False`` as to whether | +| | hydrogen atoms can be detected based on their mass. | +| | This is the default ``True``. | ++------------------------------+----------------------------------------------------------+ | com_reset_frequency | The frequency at which the ``CMMotionRemover`` acts to | | | remove center of mass relative motion. If this is not | | | set (the default) then center of mass motion is not | | | removed. | +------------------------------+----------------------------------------------------------+ | constraint | Type of constraint to use for bonds and/or angles. | -| | Valid strings are ``none``, ``h-bonds``, ``bonds``, | -| | ``h-bonds-h-angles`` and ``bonds-h-angles``. | +| | Valid strings are ``none``, ``h-bonds``, | +| | ``h-bonds-not-perturbed``, | +| | ``h-bonds-not-heavy-perturbed``, | +| | ``bonds``, ``bonds-not-perturbed``, | +| | ``bonds-not-heavy-perturbed``, | +| | ``h-bonds-h-angles``, | +| | ``h-bonds-h-angles-not-perturbed``, | +| | ``h-bonds-h-angles-not-heavy-perturbed``, | +| | ``bonds-h-angles``, | +| | ``bonds-h-angles-not-perturbed`` and | +| | ``bonds-h-angles-not-heavy-perturbed | +------------------------------+----------------------------------------------------------+ | coulomb_power | The coulomb power parameter used by the softening | -| | potential used to soften interactions involving | -| | ghost atoms. | +| | potential used to soften electrostatic interactions | +| | involving ghost atoms. This defaults to 0. | +------------------------------+----------------------------------------------------------+ | cutoff | Size of the non-bonded cutoff, e.g. | | | ``7.5*sr.units.angstrom`` | @@ -105,6 +130,9 @@ Available keys and allowable values are listed below. | dielectric | Dielectric value if a reaction field cutoff is used, | | | e.g. ``78.0`` | +------------------------------+----------------------------------------------------------+ +| dynamic_constraints | Whether or not the constraints applied to perturbable | +| | bonds should be updated with λ (defaults to ``True``). | ++------------------------------+----------------------------------------------------------+ | fixed | The atoms in the system that should be fixed (not moved) | +------------------------------+----------------------------------------------------------+ | ignore_perturbations | Whether or not to ignore any perturbations and only set | @@ -128,6 +156,11 @@ Available keys and allowable values are listed below. | lambda | The λ-value at which to set up the system (assuming this | | | contains any perturbable molecules or restraints) | +------------------------------+----------------------------------------------------------+ +| perturbable_constraint | The constraint to use for perturbable molecules. These | +| | are the same options as ``constraint``, and will | +| | override that choice for perturbable molecules if this | +| | is set. | ++------------------------------+----------------------------------------------------------+ | platform | Any valid OpenMM platform string, e.g. ``CUDA``, | | | ``OpenCL``, ``Metal``, ```CPU``, ``Reference`` | +------------------------------+----------------------------------------------------------+ @@ -143,16 +176,24 @@ Available keys and allowable values are listed below. | schedule | The :class:`~sire.cas.LambdaSchedule` to use that | | | controls how parameters are modified with λ | +------------------------------+----------------------------------------------------------+ +| shift_coulomb | The coulomb delta parameter used by the softening | +| | potential used to soften electrostatic interactions | +| | involving ghost atoms. This defaults to 1.0 Ã…. | ++------------------------------+----------------------------------------------------------+ | shift_delta | The shift_delta parameter to use for the softening | -| | potential used to soften interactions involving | -| | ghost atoms. | +| | potential used to soften LJ interactions involving | +| | ghost atoms. This defaults to 2.0 Ã…. | +------------------------------+----------------------------------------------------------+ | space | Space in which the simulation should be conducted, e.g. | | | `sr.vol.Cartesian` | +------------------------------+----------------------------------------------------------+ | swap_end_states | Whether to swap the end states of a perturbable molecule | | | (i.e. treat the perturbed state as the reference state | -| | and vice versa). | +| | and vice versa). This defaults to False. | ++------------------------------+----------------------------------------------------------+ +| taylor_power | The taylor power parameter used by the taylor algorithm | +| | for the softening potential used to soften LJ | +| | interactions involving ghost atoms. This defaults to 1. | +------------------------------+----------------------------------------------------------+ | temperature | Any temperature value, e.g. ``25*sr.units.celsius`` | +------------------------------+----------------------------------------------------------+ @@ -167,6 +208,15 @@ Available keys and allowable values are listed below. | use_dispersion_correction | Whether or not to use the dispersion correction to | | | deal with cutoff issues. This is very expensive. | +------------------------------+----------------------------------------------------------+ +| use_taylor_softening | Whether or not to use the taylor algorithm to soften | +| | interactions involving ghost atoms. This defaults to | +| | False. | ++------------------------------+----------------------------------------------------------+ +| use_zacharias_softening | Whether or not to use the zacharias algorithm to soften | +| | interactions involving ghost atoms. This defaults to | +| | True. Note that one of zacharias or taylor softening | +| | must be True, with zacharias taking precedence. | ++------------------------------+----------------------------------------------------------+ Higher level API ---------------- diff --git a/doc/source/cheatsheet/units.rst b/doc/source/cheatsheet/units.rst index b78141c5f..37842e13e 100644 --- a/doc/source/cheatsheet/units.rst +++ b/doc/source/cheatsheet/units.rst @@ -40,6 +40,31 @@ and has a specially-built grammar to combine units together >>> print(sr.u("300 nm ps-2")) 3000 Ã… ps-2 +.. note:: + + Most functions that take a unit as an argument will automatically + convert strings to units using :func:`sire.u`. This means that you + often won't need to call :func:`sire.u` explicitly. + +Unit vectors +------------ + +In addition to scalars, :mod:`sire` also supports unit vectors for +lengths, velocities and forces. You can construct these using the +:func:`sire.v` function. This accepts numbers, strings, lists, +dictionaries or anything that can be converted via :func:`sire.u`. + +>>> print(sr.v(1, 2, 3)) +( 1 Ã…, 2 Ã…, 3 Ã… ) +>>> print(sr.v("1 A", "2 A", "3 A")) +( 1 Ã…, 2 Ã…, 3 Ã… ) +>>> print(sr.v(3, 4, 5, units="A")) +( 3 Ã…, 4 Ã…, 5 Ã… ) +>>> print(sr.v(x="1 A ps-1", y="2 A ps-1", z="3 A ps-1")) +( 1 Ã… ps-1, 2 Ã… ps-1, 3 Ã… ps-1 ) +>>> print(sr.v([3,4,5], units="A ps-1")) +( 3 Ã… ps-1, 4 Ã… ps-1, 5 Ã… ps-1 ) + Converting to other units ------------------------- diff --git a/doc/source/cheatsheet/view.rst b/doc/source/cheatsheet/view.rst index 3be41f667..b8c4ae84d 100644 --- a/doc/source/cheatsheet/view.rst +++ b/doc/source/cheatsheet/view.rst @@ -494,6 +494,22 @@ the ``trajectory`` function when you want to view. ``mols.trajectory().view(wrap=False)``, or the easiest ``mols.view(wrap=False)``. +Setting the background color +---------------------------- + +You can set the background color of the view using the ``bgcolor`` argument. +This should be a string, using any color that is recognised by NGLView +via the ``backgroundColor`` stage parameter. + +>>> mols[0].view(bgcolor="black") + +.. image:: images/view_06.jpg + :alt: 3D view of aladip + +.. note:: + + The default background color is ``black``. + Closer integration with NGLView ------------------------------- diff --git a/doc/source/quickstart/index.rst b/doc/source/quickstart/index.rst index 81e78d2d2..b1dd08713 100644 --- a/doc/source/quickstart/index.rst +++ b/doc/source/quickstart/index.rst @@ -243,8 +243,7 @@ molecule that uses a λ-coordinate to morph between ethane and methanol. We'll now select the reference state (ethane)... ->>> for mol in mols.molecules("molecule property is_perturbable"): -... mols.update(mol.perturbation().link_to_reference().commit()) +>>> mols = sr.morph.link_to_reference(mols) To calculate a free energy, we would need to run multiple simulations across λ, calculating the difference in energy between neighbouring diff --git a/doc/source/tutorial/index.rst b/doc/source/tutorial/index.rst index 232078d85..bcb473c25 100644 --- a/doc/source/tutorial/index.rst +++ b/doc/source/tutorial/index.rst @@ -27,3 +27,4 @@ please :doc:`ask for support. <../support>` index_part04 index_part05 index_part06 + index_part07 diff --git a/doc/source/tutorial/index_part07.rst b/doc/source/tutorial/index_part07.rst new file mode 100644 index 000000000..35766ff77 --- /dev/null +++ b/doc/source/tutorial/index_part07.rst @@ -0,0 +1,24 @@ +=========================================== +Part 7 - Merged Molecules and Lambda Levers +=========================================== + +In the last chapter we learned how to use :mod:`sire` to set up and +run alchemical free energy calculations. We saw how the concept of a +merged molecule, together with a lambda lever, enabled us to define +a perturbation between two end states, and then run a set of +OpenMM GPU-accelerated molecular dynamics simulations to calculate +the free energy between those states. + +In this chapter we will dive deeper into the machinery of :mod:`sire` +that supports the creation and management of merged molecules. We will +see how this, plus more advanced use of lambda levers enables the +running of more complex free energy simulations. These include +those to calculate absolute binding free energies, calculate free +energies of perturbing individual residues in proteins, and +calculating free energies that involve breaking rings in ligands. + +.. toctree:: + :maxdepth: 1 + + part07/01_perturbation + part07/02_levers diff --git a/doc/source/tutorial/part06/01_merge.rst b/doc/source/tutorial/part06/01_merge.rst index 9d265901a..d6ea820d7 100644 --- a/doc/source/tutorial/part06/01_merge.rst +++ b/doc/source/tutorial/part06/01_merge.rst @@ -167,7 +167,7 @@ The :class:`~sire.morph.Perturbation` class provides the be used to link all of the standard properties to either the reference or perturbed values. ->>> mol = pert.link_to_reference().commit() +>>> mol = pert.link_to_reference() >>> mol.view() .. image:: images/06_01_01.jpg @@ -175,7 +175,7 @@ or perturbed values. has viewed the reference state (ethane), while ->>> mol = pert.link_to_perturbed().commit() +>>> mol = pert.link_to_perturbed() >>> mol["not element Xx"].view() .. image:: images/06_01_02.jpg @@ -200,12 +200,12 @@ merged molecule in its environment by updating the system with the result of linking the molecule to either the reference or perturbed states, e.g. ->>> mols = mols.update(pert.link_to_reference().commit()) +>>> mols = mols.update(pert.link_to_reference()) has updated the system with a copy of the merged molecule where all of its standard properties are linked to the reference state. While ->>> mols = mols.update(pert.link_to_perturbed().commit()) +>>> mols = mols.update(pert.link_to_perturbed()) updates the system with a copy of the merged molecule where all of its standard properties are linked to the perturbed state. @@ -214,12 +214,22 @@ In general, a system could contain many merged molecules. To link all of them to the reference state you could use >>> for mol in mols.molecules("molecule property is_perturbable"): -... mols.update(mol.perturbation().link_to_reference().commit()) +... mols.update(mol.perturbation().link_to_reference()) or to link all of them to the perturbed state you could use >>> for mol in mols.molecules("molecule property is_perturbable"): -... mols.update(mol.perturbation().link_to_perturbed().commit()) +... mols.update(mol.perturbation().link_to_perturbed()) + +The :func:`sire.morph.link_to_reference` and +:func:`sire.morph.link_to_perturbed` convenience function can do this +for you, e.g. + +>>> mols = sr.morph.link_to_reference(mols) + +or + +>>> mols = sr.morph.link_to_perturbed(mols) Now you could view and manipulate them as normal, e.g. using ``mols.view()`` etc. diff --git a/doc/source/tutorial/part06/02_alchemical_dynamics.rst b/doc/source/tutorial/part06/02_alchemical_dynamics.rst index 2368c1c71..52c4861ab 100644 --- a/doc/source/tutorial/part06/02_alchemical_dynamics.rst +++ b/doc/source/tutorial/part06/02_alchemical_dynamics.rst @@ -42,8 +42,7 @@ For example, we could minimise our alchemical system at λ=0.5 using >>> import sire as sr >>> mols = sr.load(sr.expand(sr.tutorial_url, "merged_molecule.s3")) ->>> for mol in mols.molecules("molecule property is_perturbable"): -... mols.update(mol.perturbation().link_to_reference().commit()) +>>> mols = sr.morph.link_to_reference(mols) >>> mols = mols.minimisation(lambda_value=0.5).run().commit() We can then run some dynamics at this λ-value using @@ -238,8 +237,8 @@ In this case the levers are all identical, so would change the parameter in the same way. You can choose your own equation for the λ-schedule. For example, maybe we want to scale the charge by the square of λ. ->>> s.set_equation("morph", "charge", -... s.lam()**2 * s.final() + s.initial() * (1 - s.lam()**2)) +>>> s.set_equation(stage="morph", lever="charge", +... equation=s.lam()**2 * s.final() + s.initial() * (1 - s.lam()**2)) >>> print(s) LambdaSchedule( morph: λ * final + initial * (-λ + 1) @@ -267,7 +266,8 @@ would append a second stage, called ``scale``, which by default would use the ``final`` value of the parameter. We could then add a lever to this stage that scales down the charge to 0, ->>> s.set_equation("scale", "charge", (1-s.lam()) * s.final()) +>>> s.set_equation(stage="scale", lever="charge", +... equation=(1-s.lam()) * s.final()) >>> print(s) LambdaSchedule( morph: λ * final + initial * (-λ + 1) diff --git a/doc/source/tutorial/part06/04_alchemical_restraints.rst b/doc/source/tutorial/part06/04_alchemical_restraints.rst index cae1f5579..5ce973387 100644 --- a/doc/source/tutorial/part06/04_alchemical_restraints.rst +++ b/doc/source/tutorial/part06/04_alchemical_restraints.rst @@ -50,7 +50,7 @@ will always evaluate to 100% of the restraint for all values of λ during the ``morph`` stage. You can make sure that the restraint is kept at 100% during this stage by setting this value explicitly; ->>> l.set_equation("morph", "restraint", l.initial()) +>>> l.set_equation(stage="morph", lever="restraint", l.initial()) >>> print(l) LambdaSchedule( restraints_on: initial @@ -119,14 +119,16 @@ their names. We will first scale up the ``distance`` restraint in a >>> l = sr.cas.LambdaSchedule() >>> l.add_stage("distance_restraints", 0) ->>> l.set_equation("distance_restraints", "distance", l.lam() * l.initial()) +>>> l.set_equation(stage="distance_restraints", lever="distance", +... equation=l.lam() * l.initial()) and will then scale up the ``positional`` restraint in a ``positional_restraints`` stage, while keeping the ``distance`` restraint fully on. >>> l.add_stage("positional_restraints", 1) ->>> l.set_equation("positional_restraints", "positional", l.lam() * l.initial()) +>>> l.set_equation(stage="positional_restraints", lever="positional", +... equation=l.lam() * l.initial()) >>> print(l) LambdaSchedule( distance_restraints: 0 diff --git a/doc/source/tutorial/part06/05_free_energy_perturbation.rst b/doc/source/tutorial/part06/05_free_energy_perturbation.rst index 0023f2d1c..27fef7e95 100644 --- a/doc/source/tutorial/part06/05_free_energy_perturbation.rst +++ b/doc/source/tutorial/part06/05_free_energy_perturbation.rst @@ -27,8 +27,7 @@ System( name=BioSimSpace_System num_molecules=4054 num_residues=4054 num_atoms=1 And lets link the properties to the reference state, so that we start the simulation using the reference state coordinates. ->>> for mol in mols.molecules("molecule property is_perturbable"): -... mols.update(mol.perturbation().link_to_reference().commit()) +>>> mols = sr.morph.link_to_reference(mols) Next we will run through 21 evenly-space λ values from 0 to 1. We've picked 21 because it is a reasonable number that should over-sample the λ-coordinate. @@ -227,7 +226,7 @@ instead of ``energy_{lambda}.s3``). >>> import sire as sr >>> mols = sr.load(sr.expand(sr.tutorial_url, "merged_molecule.s3")) >>> mol = mols.molecule("molecule property is_perturbable") ->>> mol.update(mol.perturbation().link_to_reference().commit()) +>>> mol = sr.morph.link_to_reference(mol) >>> for l in range(0, 105, 5): ... # turn l into the lambda value by dividing by 100 ... lambda_value = l / 100.0 diff --git a/doc/source/tutorial/part06/06_faster_fep.rst b/doc/source/tutorial/part06/06_faster_fep.rst index 5e600ba6e..faf1c0ad3 100644 --- a/doc/source/tutorial/part06/06_faster_fep.rst +++ b/doc/source/tutorial/part06/06_faster_fep.rst @@ -35,8 +35,7 @@ after loading the molecules and minimising, >>> import sire as sr >>> mols = sr.load(sr.expand(sr.tutorial_url, "merged_molecule.s3")) ->>> for mol in mols.molecules("molecule property is_perturbable"): -... mols.update(mol.perturbation().link_to_reference().commit()) +>>> mols = sr.morph.link_to_reference(mols) >>> mols = mols.minimisation().run().commit() ...we can turn on constraints of the bonds involving hydrogen atoms by @@ -304,16 +303,23 @@ still use a large timestep (e.g. 3 fs or 4 fs). This is because the hydrogens on the perturbable molecules would become heavier, and so the vibrations of those atoms should have a lower frequency. +To aid this, we can use the ``h-bonds-not-perturbed`` constraint. This will +constrain all bonds involving hydrogen atoms, except for those involving +atoms that are directly perturbed. This means that all of the non-perturbing +bonds in the perturbable molecule will be constrained, which should aid +simulation stability for large timesteps. + >>> mols.update(repartitioned_mol) >>> d = mols.dynamics(timestep="4fs", temperature="25oC", -... constraint="h-bonds", +... constraint="h-bonds-not-perturbed", ... perturbable_constraint="none") >>> d.run("5ps") >>> print(d) Dynamics(completed=5 ps, energy=-31950.1 kcal mol-1, speed=100.8 ns day-1) This has given us the best of both worlds - a fast simulation with a larger -timestep, plus no constraints on the perturbable molecules. +timestep, plus no constraints on the bonds that perturb +in the perturbable molecules. Using this protocol, we can now recalculate the relative free energy of ethane and methanol. @@ -324,13 +330,11 @@ ethane and methanol. ... print(f"Simulating lambda={lambda_value:.2f}") ... # minimise the system at this lambda value ... min_mols = mols.minimisation(lambda_value=lambda_value, -... constraint="h-bonds", -... perturbable_constraint="none").run().commit() +... constraint="h-bonds-not-perturbed").run().commit() ... # create a dynamics object for the system ... d = min_mols.dynamics(timestep="4fs", temperature="25oC", ... lambda_value=lambda_value, -... constraint="h-bonds", -... perturbable_constraint="none") +... constraint="h-bonds-not-perturbed") ... # generate random velocities ... d.randomise_velocities() ... # equilibrate, not saving anything @@ -376,6 +380,17 @@ a little less time. (e.g. 250 ps per λ-window) or by running multiple repeats and taking and average. +Switching to Reaction Field from PME +------------------------------------ + +By default, the dynamics simulation uses the particle mesh Ewald (PME) +method to calculate long-range electrostatics. This is a very accurate, +but comes with a high computational cost. We can switch to the faster +reaction field method by setting the ``cutoff_type`` option to +``RF``. This cutoff type works well for perturbations that don't involve +a change in net charge (as most perturbations). Switching to RF can +improve the simulation speed by 25-50%. + .. _ExampleFEPScript: Complete Example Script @@ -384,8 +399,8 @@ Complete Example Script Putting everything together, here is a simple script that does all of the work of calculating the relative hydration free energy of ethane and methanol. The key parameters (e.g. timestep, constraint, run time, -λ-values etc) are pulled out as variables at the top. The script then -runs a dynamics simulation for each λ-window for the water leg, then +cutoff type, λ-values etc) are pulled out as variables at the top. The script +then runs a dynamics simulation for each λ-window for the water leg, then a dynamics simulation using the same parameters and λ-windows for the vacuum leg. The free energies are collected and then calculated using BAR from alchemlyb. This is a good starting point for you to adapt for your diff --git a/doc/source/tutorial/part06/scripts/run_md.py b/doc/source/tutorial/part06/scripts/run_md.py index ab26f839b..912455e5d 100644 --- a/doc/source/tutorial/part06/scripts/run_md.py +++ b/doc/source/tutorial/part06/scripts/run_md.py @@ -1,25 +1,27 @@ import sire as sr -mols = sr.load(sr.expand(sr.tutorial_url, "merged_molecule.s3")) - -for mol in mols.molecules("molecule property is_perturbable"): - mol = mol.perturbation().link_to_reference().commit() - mol = sr.morph.repartition_hydrogen_masses(mol, mass_factor=1.5) - mols.update(mol) - -mols = mols.minimisation().run().commit() - timestep = "4fs" -energy_frequency = "0.1ps" +energy_frequency = "0.4ps" + +constraint = "h-bonds-not-perturbed" +perturbable_constraint = "h-bonds-not-perturbed" -constraint = "h-bonds" -perturbable_constraint = None +cutoff_type = "RF" equil_time = "2ps" run_time = "25ps" lambda_values = [x / 100.0 for x in range(0, 101, 5)] +mols = sr.load(sr.expand(sr.tutorial_url, "merged_molecule.s3")) + +for mol in mols.molecules("molecule property is_perturbable"): + mol = sr.morph.link_to_reference(mol) + mol = sr.morph.repartition_hydrogen_masses(mol, mass_factor=1.5) + mols.update(mol) + +mols = mols.minimisation(cutoff_type=cutoff_type).run().commit() + print("\nWater leg") for i, lambda_value in enumerate(lambda_values): @@ -27,6 +29,7 @@ # minimise the system at this lambda value min_mols = ( mols.minimisation( + cutoff_type=cutoff_type, lambda_value=lambda_value, constraint=constraint, perturbable_constraint="none", @@ -39,6 +42,7 @@ d = min_mols.dynamics( timestep=timestep, temperature="25oC", + cutoff_type=cutoff_type, lambda_value=lambda_value, constraint=constraint, perturbable_constraint=perturbable_constraint, diff --git a/doc/source/tutorial/part07/01_perturbation.rst b/doc/source/tutorial/part07/01_perturbation.rst new file mode 100644 index 000000000..e267b6790 --- /dev/null +++ b/doc/source/tutorial/part07/01_perturbation.rst @@ -0,0 +1,319 @@ +============ +Perturbation +============ + +As seen in :doc:`chapter 6 <../part06/01_merge>`, the concept of a +"merged molecule" is central to how :mod:`sire` implements the +perturbations (morphing) needed for free energy calculations. + +A merged molecule is one that has been created with a single set of atoms, +but with properties that represent either a "reference" (λ=0) or "perturbed" +(λ=1) state. + +For example, let's load a merged molecule that represents +neopentane in the reference state, and methane in the perturbed state. + +>>> import sire as sr +>>> mols = sr.load_test_files("neo_meth_scratch.bss") +>>> print(mols) +System( name=BioSimSpace_System num_molecules=1 num_residues=1 num_atoms=17 ) +>>> print(mols[0]) +Molecule( Merged_Molecule:6 num_atoms=17 num_residues=1 ) + +.. note:: + + This molecule was created using `BioSimSpace `_. + We will cover how to create merged molecules in a later section + in this tutorial. + +Examining the perturbation +-------------------------- + +Normally, a :mod:`sire` molecule will have one set of forcefield parameters. +For example, the atomic charges would be stored in the ``charge`` property, +the Lennard-Jones parameters in the ``LJ`` property, the bond parameters in +the ``bond`` property, etc. + +However, a merged molecule has two sets of parameters, one for the reference +state (representing λ=0) and one for the perturbed state (representing λ=1). +The reference state parameters are stored in the ``0`` properties, e.g. +``charge0``, ``LJ0``, ``bond0``, etc. The perturbed state parameters are +stored in the ``1`` properties, e.g. ``charge1``, ``LJ1``, ``bond1``, etc. + +We can see this for our merged molecule above by printing out all of the +property keys. + +>>> print(mols[0].property_keys()) +['gb_radii0', 'gb_radii1', 'atomtype0', 'element0', 'atomtype1', 'element1', + 'time', 'intrascale0', 'intrascale1', 'improper0', 'mass0', 'improper1', + 'mass1', 'ambertype0', 'ambertype1', 'treechain0', 'treechain1', + 'parameters0', 'parameters1', 'space', 'charge0', 'charge1', + 'connectivity', 'dihedral0', 'forcefield0', 'dihedral1', 'coordinates0', + 'forcefield1', 'coordinates1', 'LJ0', 'name0', 'LJ1', 'name1', 'angle0', + 'angle1', 'bond0', 'bond1', 'molecule0', 'molecule1', 'gb_screening0', + 'gb_screening1', 'is_perturbable'] + +Notice how there is a ``0`` and ``1`` version of nearly every property. +These represent the molecule at the reference and perturbed end states, e.g. + +>>> print(mols[0].property("charge0")) +SireMol::AtomCharges( size=17 +0: -0.0853353 |e| +1: -0.0602353 |e| +2: -0.0853353 |e| +3: -0.0853353 |e| +4: -0.0853353 |e| +... +12: 0.0334647 |e| +13: 0.0334647 |e| +14: 0.0334647 |e| +15: 0.0334647 |e| +16: 0.0334647 |e| +) +>>> print(mols[0].property("charge1")) +SireMol::AtomCharges( size=17 +0: 0 |e| +1: 0.0271 |e| +2: 0 |e| +3: -0.1084 |e| +4: 0 |e| +... +12: 0.0271 |e| +13: 0.0271 |e| +14: 0 |e| +15: 0 |e| +16: 0 |e| +) + +prints the charges for neopentane, and then methane. Note how many of the +atom charges for methane are zero. This is because methane has fewer +atoms than neopentane, and so the charges (and other parameters) for the +extra atoms are "switched off" for the perturbed state. These extra atoms +are often called "dummy atoms". In :mod:`sire`, we prefer to call them +"ghost atoms". They are "ghosts" because they fade away to nothing as +λ increases from 0 to 1. + +In addition to the ``0`` and ``1`` properties, a perturbable molecule must +also contain the ``is_perturbable`` property. This is a boolean that signals +whether or not a molecule is perturbable. It should be ``True`` if the molecule +can be perturbed. + +>>> print(mols[0].property("is_perturbable")) +True + +Perturbation objects +-------------------- + +Examining the perturbable properties directly can be a little cumbersome. +To make things easier, there are a number of helper classes and functions. +These are accessed via the :mod:`sire.morph` module, and the +:func:`~sire.mol.Molecule.perturbation` function of a :class:`~sire.mol.Molecule`. + +Let's create the :class:`~sire.morph.Perturbation` object for our molecule. + +>>> pert = mols[0].perturbation() +>>> print(pert) +Perturbation( Molecule( Merged_Molecule:6 num_atoms=17 num_residues=1 ) ) + +We can extract a molecule that contains only the reference or perturbed +parameters using the :func:`~sire.morph.Perturbation.extract_reference` and +:func:`~sire.morph.Perturbation.extract_perturbed` functions. + +>>> ref = pert.extract_reference() +>>> print(ref.property_keys()) +['bond', 'atomtype', 'time', 'intrascale', 'element', 'dihedral', + 'parameters', 'ambertype', 'angle', 'gb_radii', 'treechain', 'space', + 'forcefield', 'name', 'connectivity', 'gb_screening', 'molecule', + 'coordinates', 'LJ', 'charge', 'mass', 'improper'] +>>> print(ref.property("charge")) +SireMol::AtomCharges( size=17 +0: -0.0853353 |e| +1: -0.0602353 |e| +2: -0.0853353 |e| +3: -0.0853353 |e| +4: -0.0853353 |e| +... +12: 0.0334647 |e| +13: 0.0334647 |e| +14: 0.0334647 |e| +15: 0.0334647 |e| +16: 0.0334647 |e| +) + +.. note:: + + You can extract the reference and perturbed molecules in a collection + using the :func:`sire.morph.extract_reference` and + :func:`sire.morph.extract_perturbed` functions. For example, + ``mols = sire.morph.extract_reference(mols)`` would extract the + reference state of all molecules in ``mols``. + +.. note:: + + By default, ghost atoms will be removed when you extract an end state. + You can retain ghost atoms by passing ``remove_ghosts=False`` to the + above ``extract_reference`` and ``extract_perturbed`` functions. + +Extracting the reference or perturbed states can be useful if you want to +go back to the unmerged molecule, e.g. for visualisation via the +:func:`~sire.mol.SelectorMol.view` function. However, normally we would want to +keep the properties of the two end states, and then choose one end state +as the "current" state. We can do this by creating links from the +"standard" property names (e.g. ``charge``, ``LJ`` etc.) to the equivalent +properties of the chosen end state. You could do this manually, but it is +much easier to use the :func:`~sire.morph.Perturbation.link_to_reference` +and :func:`~sire.morph.Perturbation.link_to_perturbed` functions. + +For example, here we will link to the perturbed state. + +>>> mol = pert.link_to_perturbed() +>>> print(mol.get_links()) +{'improper': 'improper1', 'gb_screening': 'gb_screening1', + 'mass': 'mass1', 'dihedral': 'dihedral1', 'parameters': 'parameters1', + 'treechain': 'treechain1', 'bond': 'bond1', 'ambertype': 'ambertype1', + 'molecule': 'molecule1', 'atomtype': 'atomtype1', 'charge': 'charge1', + 'angle': 'angle1', 'forcefield': 'forcefield1', + 'coordinates': 'coordinates1', 'intrascale': 'intrascale1', + 'name': 'name1', 'LJ': 'LJ1', 'element': 'element1', + 'gb_radii': 'gb_radii1'} +>>> print(mol.property("charge")) +SireMol::AtomCharges( size=17 +0: 0 |e| +1: 0.0271 |e| +2: 0 |e| +3: -0.1084 |e| +4: 0 |e| +... +12: 0.0271 |e| +13: 0.0271 |e| +14: 0 |e| +15: 0 |e| +16: 0 |e| +) + +.. note:: + + You can link the reference or perturbed states in a collection of + molecules using the :func:`sire.morph.link_to_reference` and + :func:`sire.morph.link_to_perturbed` functions. For example, + ``mols = sire.morph.link_to_perturbed(mols)`` would link all molecules + in ``mols`` to the perturbed state. + +.. note:: + + It is a good idea when loading a system containing one or more merged + molecules to decide on which end state you want to use as the "current". + After loading, simply call either + ``mols = sire.morph.link_to_reference(mols)`` or + ``mols = sire.morph.link_to_perturbed(mols)`` to update the molecules + with your chosen state. + +Inspecting the changing parameters +---------------------------------- + +Under the hood, the properties of the two end states are converted into +parameters for the underlying OpenMM system used for the +free energy simulations. You can access these parameters by converting +the above perturbation into a +:class:`~sire.convert.openmm.PerturbableOpenMMMolecule`. + +>>> pert_omm = pert.to_openmm() +>>> print(pert_omm) +PerturbableOpenMMMolecule() + +This object contains all of the parameters needed to represent both +end states of this molecule in the OpenMM forces. For example, +:func:`~sire.convert.openmm.PerturbableOpenMMMolecule.get_charges0` +returns a list of the charges for the reference +state, while +:func:`~sire.convert.openmm.PerturbableOpenMMMolecule.get_charges0` +returns a list of the charges for the perturbed state. + +>>> print(pert_omm.get_charges0()) +[-0.08533529411764705, -0.06023529411764705, -0.08533529411764705, + -0.08533529411764705, -0.08533529411764705, 0.03346470588235294, + 0.03346470588235294, 0.03346470588235294, 0.03346470588235294, + 0.03346470588235294, 0.03346470588235294, 0.03346470588235294, + 0.03346470588235294, 0.03346470588235294, 0.03346470588235294, + 0.03346470588235294, 0.03346470588235294] + +At this level, we are most interested in the parameters that change as +we morph from the reference to the perturbed state (i.e. as we move +from λ=0 to λ=1). You can access these via the +:func:`~sire.convert.openmm.PerturbableOpenMMMolecule.changed_atoms`, +:func:`~sire.convert.openmm.PerturbableOpenMMMolecule.changed_bonds`, +:func:`~sire.convert.openmm.PerturbableOpenMMMolecule.changed_angles`, +:func:`~sire.convert.openmm.PerturbableOpenMMMolecule.changed_torsions`, +:func:`~sire.convert.openmm.PerturbableOpenMMMolecule.changed_exceptions` and +:func:`~sire.convert.openmm.PerturbableOpenMMMolecule.changed_constraints` +functions. + +>>> print(pert_omm.changed_bonds()) + bond length0 length1 k0 k1 +0 C2:2-C4:4 0.15375 0.10969 251793.12 276646.08 +>>> print(pert_omm.changed_angles()) + angle size0 size1 k0 k1 +0 C2:2-C4:4-H12:12 1.916372 1.877626 387.4384 329.6992 +1 C2:2-C4:4-H14:14 1.916372 1.877626 387.4384 329.6992 +2 C2:2-C4:4-H13:13 1.916372 1.877626 387.4384 329.6992 + +.. note:: + + The parameters are directly as would be used in an OpenMM force, + i.e. in OpenMM default units. + +Here we see that the perturbation involves the C2-C4 bond changing +from 0.15375 nm to 0.10969 nm, with an associated change in the +force constant from 251793.12 kJ mol^-1 nm^-2 to 276646.08 kJ mol^-1 nm^-2. + +Similarly, the C2-C4-H12 angle changes from 1.916372 radians to 1.877626 radians, +with an associated change in the force constant from 387.4384 kJ mol^-1 rad^-2 +to 329.6992 kJ mol^-1 rad^-2. + +These functions return their output as pandas dataframes. You can get a raw +output by passing in ``to_pandas=False``. + +>>> print(pert_omm.changed_bonds(to_pandas=False)) +[(Bond( C2:2 => C4:4 ), 0.15375000000000003, 0.10969000000000001, 251793.11999999997, 276646.08)] + +As well as forcefield parameters, you can also access any changes in +constraint parameters caused by constraining perturbable bonds. For example, +here we can create the :class:`~sire.convert.openmm.PerturbableOpenMMMolecule` +used when the ``bonds`` constraint algorithm is used. + +>>> pert_omm = pert.to_openmm(constraint="bonds") + +Now, we can see how the constraint parameters will change across λ using +the :func:`~sire.convert.openmm.PerturbableOpenMMMolecule.changed_constraints` +function. + +>>> print(pert_omm.changed_constraints()) + atompair length0 length1 +0 C2:2-C4:4 0.15375 0.10969 + +In this case, we see that the perturbing C2-C4 bond is constrained, with a +constraint length of 0.15375 nm in the reference state, and 0.10969 nm in the +perturbed state. + +Visualising the perturbation +---------------------------- + +Perturbations can involve changes in bond lengths, or angle / torsion sizes. +These can be difficult to visualise from the raw numbers. To help with this, +the :func:`~sire.morph.Perturbation.view_reference` and +:func:`~sire.morph.Perturbation.view_perturbed` functions can be used to +view a 3D movie of the perturbation from either the reference or perturbed +states. + +>>> pert.view_reference() + +.. image:: images/07_01_01.jpg + :alt: View of the movie showing the perturbation from neopentane to methane + +.. note:: + + The movie loops from λ=0 to λ=1, and then back to λ=0. You can pass in + any of the visualisation options as used in the standard + :func:`~sire.mol.SelectorMol.view` function. The viewer may show some + bonds as broken - this is just because they are too long to be shown + when calculated at λ=0. diff --git a/doc/source/tutorial/part07/02_levers.rst b/doc/source/tutorial/part07/02_levers.rst new file mode 100644 index 000000000..24d3ac3e2 --- /dev/null +++ b/doc/source/tutorial/part07/02_levers.rst @@ -0,0 +1,231 @@ +=========================== +Lambda schedules and levers +=========================== + +In :doc:`the last chapter <../part06/02_alchemical_dynamics>` we saw how +we could use a :class:`~sire.cas.LambdaSchedule` to control how the +forcefield parameters of perturbable molecules are morphed as a function +of λ. + +For example, let's load up a merged molecule system that represents +neopentane to methane in water, and then explore the +:class:`~sire.cas.LambdaSchedule` that is associated with this system. + +>>> import sire as sr +>>> mols = sr.load_test_files("neo_meth_solv.bss") +>>> mols = sr.morph.link_to_reference(mols) +>>> print(mols) +System( name=BioSimSpace_System num_molecules=879 num_residues=879 num_atoms=2651 ) +>>> d = mols.dynamics() +>>> s = d.get_schedule() +>>> print(s) +LambdaSchedule( + morph: (-λ + 1) * initial + λ * final +) + +This shows that all perturbable parameters of all perturbable molecules in this +system will be morphed in a single stage (called "morph"), using a linear +interpolation between the initial and final values of the parameters. + +As we saw in the :doc:`last section <01_perturbation>`, we can find the exact +values of all of the perturbable parameters of a perturbable molecle via +the perturbation object. + +>>> p = mols[0].perturbation() +>>> print(p) +Perturbation( Molecule( Merged_Molecule:6 num_atoms=17 num_residues=1 ) ) +>>> p_omm = p.to_openmm() +>>> print(p_omm.changed_bonds()) + bond length0 length1 k0 k1 +0 C2:2-C4:4 0.15375 0.10969 251793.12 276646.08 + +.. note:: + + In this case, we know that only the first molecule is perturbable, + hence why it is safe to use ``mols[0].perturbation()``. In general, + you would need to find perturbable molecule(s), e.g. using + ``pert_mols = mols.molecules("property is_perturbable")``. + +From this, we can see that the bond between atoms C2 and C4 is perturbable, +and the above schedule will morph the bond length from 0.15375 nm to 0.10969 nm, +and the force constant from 251793.12 kJ mol-1 nm-2 to 276646.08 kJ mol-1 nm-2, +linearly with respect to λ. + +Controlling individual levers +----------------------------- + +We can also control how individual parameters in individual forces are +morphed. We call these individual morphing controls "levers". +So, the bond potential has two levers, its bond length, and its force constant. + +We can list the levers that are available in the OpenMM context using the +:meth:`~sire.cas.LambdaSchedule.get_levers` function. + +>>> print(s.get_levers()) +['charge', 'sigma', 'epsilon', 'charge_scale', 'lj_scale', + 'bond_length', 'bond_k', 'angle_size', 'angle_k', 'torsion_phase', + 'torsion_k', 'alpha', 'kappa'] + +The parameter representing bond length is connected to the ``bond_length`` lever, +while the parameter representing the bond force constant is +connected to the ``bond_k`` lever. + +We can set the morphing equation for individual levers by naming the lever +in the :meth:`~sire.cas.LambdaSchedule.set_equation` function. + +>>> l = s.lam() +>>> init = s.initial() +>>> fin = s.final() +>>> s.set_equation(stage="morph", lever="bond_length", +... equation=(1-l**2)*init + l**2*fin) +>>> print(s) +LambdaSchedule( + morph: initial * (-λ + 1) + final * λ + bond_length: initial * (-λ^2 + 1) + final * λ^2 +) + +.. note:: + + We extracted the symbols representing λ and the initial and final + states to ``l``, ``init`` and ``fin``, just to make it easier to + write the equations. + +We can see that the ``bond_length`` lever in the ``morph`` stage is now +interpolated from the initial to final value by λ^2, rather than λ. + +All of the other levers continue to use the default equation for this stage, +which is the linear interpolation between the initial and final values. + +Controlling individual levers in individual forces +-------------------------------------------------- + +Multiple OpenMM Force objects are combined in the OpenMM context to +model the total force acting on each atom in the system. OpenMM is very +flexible, and supports the arbitrary combination of lots of different +Force objects. In :mod:`sire`, we use a simple collection of Force objects +that, when combined, model perturbable systems. You can list the names +of the Force objects used via the :meth:`~sire.cas.LambdaSchedule.get_forces` +function. + +>>> print(s.get_forces()) +['clj', 'bond', 'angle', 'torsion', 'ghost/ghost', + 'ghost/non-ghost', 'ghost-14'] + +In this case, as we have a perturbable system, the Force objects used are; + +* ``bond``: `OpenMM::HarmonicBondForce `__. + This models all of the bonds between atoms in the system. It uses + parameters that are controlled by the ``bond_length`` and ``bond_k`` levers. +* ``angle``: `OpenMM::HarmonicAngleForce `__. + This models all of the angles between atoms in the system. It uses + parameters that are controlled by the ``angle_size`` and ``angle_k`` levers. +* ``torsion``: `OpenMM::PeriodicTorsionForce `__. + This models all of the torsions (dihedrals and impropers) in the system. + It uses parameters that are controlled by the ``torsion_phase`` + and ``torsion_k`` levers. +* ``clj``: `OpenMM::NonbondedForce `__. + This models all of the electrostatic (coulomb) and van der Waals (Lennard-Jones) + interactions between non-ghost atoms in the system. Non-ghost atoms are + any atoms that are not ghosts in either end state. It uses parameters that + are controlled by the ``charge``, ``sigma``, ``epsilon``, ``charge_scale`` + and ``lj_scale`` levers. +* ``ghost/ghost``: `OpenMM::CustomNonbondedForce `__. + This models all of the electrostatic (coulomb) and van der Waals (Lennard-Jones) + interactions between ghost atoms in the system. Ghost atoms are any atoms + that are ghosts in either end state. It uses parameters that are controlled + by the ``charge``, ``sigma``, ``epsilon``, ``alpha`` and ``kappa`` levers. +* ``ghost/non-ghost``: `OpenMM::CustomNonbondedForce `__. + This models all of the electrostatic (coulomb) and van der Waals (Lennard-Jones) + interactions between the ghost atoms and the non-ghost atoms in the system. + It uses parameters that are controlled + by the ``charge``, ``sigma``, ``epsilon``, ``alpha`` and ``kappa`` levers. +* ``ghost-14``: `OpenMM::CustomBondForce `__. + This models all of the 1-4 non-bonded interactions involving ghost atoms. + It uses parameters that are controlled by the ``charge``, ``sigma``, ``epsilon``, + ``alpha``, ``kappa``, ``charge_scale`` and ``lj_scale`` levers. + +Some levers, like ``bond_length``, are used only by a single Force object. +However, others, like ``charge``, are used by multiple Force objects. + +By default, setting a lever will affect the parameters in all of the Force +objects that use that lever. However, you can limit which Force objects +are affected by specifying the force in the :meth:`~sire.cas.LambdaSchedule.set_equation` +function. + +>>> s.set_equation(stage="morph", force="ghost/ghost", lever="alpha", + equation=0.5*s.get_equation("morph")) +>>> print(s) +LambdaSchedule( + morph: initial * (-λ + 1) + final * λ + bond_length: (-λ^2 + 1) * initial + final * λ^2 + ghost/ghost::alpha: 0.5 * (initial * (-λ + 1) + final * λ) +) + +Here, we have set the ``alpha`` lever in the ``ghost/ghost`` Force object +to set the ``alpha`` parameter to equal half of its linearly interpolated +value. + +.. note:: + + The ``alpha`` parameter controls the amount of softening used in the + soft-core potential for modelling ghost atoms. An ``alpha`` value of + 0.0 means that the soft-core potential is not used, while an ``alpha`` + value of 1.0 means that the soft-core potential is on and strong. + Scaling up ``alpha`` will gradually soften any ghost atoms. + +Controlling individual levers for individual molecules +------------------------------------------------------ + +We can also control how individual levers for individual forces are +morphed for individual perturbable molecules in the system. This is useful +if you have multiple perturbable molecules, and you want to control +how each one perturbs separately. + +To do this, we use the :meth:`~sire.cas.LambdaSchedule.set_molecule_schedule` function +to set the schedule for a specific perturbable molecule. + +First, let's get the original schedule for our simulation... + +>>> orig_s = d.get_schedule() +>>> print(orig_s) +LambdaSchedule( + morph: initial * (-λ + 1) + final * λ +) + +Now, let's set the schedule to be used *only* for the first perturbable +molecule in the system to the custom one we created earlier. + +>>> orig_s.set_molecule_schedule(0, s) +>>> print(orig_s) +LambdaSchedule( + morph: initial * (-λ + 1) + final * λ + Molecule schedules: + 0: LambdaSchedule( + morph: initial * (-λ + 1) + final * λ + bond_length: (-λ^2 + 1) * initial + final * λ^2 + ghost/ghost::alpha: 0.5 * (initial * (-λ + 1) + final * λ) +) +) + +This shows that the default for all perturbable molecules except the first +is to use the default morph equation for all levers in all forces. + +However, for the first perturbable molecule (which has index ``0``), +this uses our custom equation for the ``bond_length`` lever in the +``morph`` stage, and our custom equation for the ``alpha`` lever in +the ``ghost/ghost`` force in the ``morph`` stage. + +Once you are happy, we can set the schedule to be used for the simulaton +via the :meth:`~sire.mol.Dynamics.set_schedule` function. + +>>> d.set_schedule(orig_s) +>>> print(d.get_schedule()) +LambdaSchedule( + morph: initial * (-λ + 1) + final * λ + Molecule schedules: + 0: LambdaSchedule( + morph: initial * (-λ + 1) + final * λ + bond_length: (-λ^2 + 1) * initial + final * λ^2 + ghost/ghost::alpha: 0.5 * (initial * (-λ + 1) + final * λ) +) +) diff --git a/doc/source/tutorial/part07/images/07_01_01.jpg b/doc/source/tutorial/part07/images/07_01_01.jpg new file mode 100644 index 000000000..50a15dc7c Binary files /dev/null and b/doc/source/tutorial/part07/images/07_01_01.jpg differ diff --git a/requirements_bss.txt b/requirements_bss.txt index f1435328f..aa5d60ec7 100644 --- a/requirements_bss.txt +++ b/requirements_bss.txt @@ -10,10 +10,9 @@ openmmtools >= 0.21.5 -# Both ambertools and gromacs aren't available on ARM64 or Windows -# We will only require them for Linux x86-64 and Apple x86-64. -ambertools >= 22 ; sys_platform != "win32" and platform_machine == "x86_64" -gromacs ; sys_platform != "win32" and platform_machine == "x86_64" +# Both ambertools and gromacs aren't available on Windows +ambertools >= 22 ; sys_platform != "win32" +gromacs ; sys_platform != "win32" # The following are actual BioSimSpace run-time requirements. Please update # this list as new requirements are added. diff --git a/src/sire/__init__.py b/src/sire/__init__.py index 21864177e..631ae5930 100644 --- a/src/sire/__init__.py +++ b/src/sire/__init__.py @@ -51,6 +51,7 @@ "use_mixed_api", "use_new_api", "use_old_api", + "v", ] @@ -164,6 +165,142 @@ def u(unit): ) +def v(x, y=None, z=None, units=None): + """ + Return a sire vector from the passed expression. If this is a set of + numbers or lengths (or a combination) then a sire.maths.Vector will + be returned. If this is a value with velocity or force units then + a Velocity3D or Force3D will be returned. If there is no + vector type for data of this value then a simple python vector object + will be returned. + + Args: + x: The x-value, or something containing 3 values + y: The y-value (cannot be specified if x has more than 1 value) + z: The z-value (cannot be specified if x has more than 1 value) + units: The units of the passed values (optional - will be guessed + if not specified). You should not pass this if x, y or z + already have values. + """ + if type(x) is dict: + if y is not None or z is not None: + raise ValueError("You cannot specify y or z values when passing a dict.") + + y = x["x"] + z = x["z"] + x = x["x"] + + elif (not isinstance(x, str)) and hasattr(x, "__len__"): + if len(x) != 3: + raise ValueError( + "The passed list or tuple must have three elements to be " + f"converted to a Vector - the value '{x}' is not valid." + ) + + if y is not None or z is not None: + raise ValueError( + "You cannot specify y or z values when passing a list or tuple." + ) + + (x, y, z) = (x[0], x[1], x[2]) + + else: + if y is None: + y = 0 + + if z is None: + z = 0 + + if units is not None: + u_units = u(units) + + if u_units.temperature() == 0: + x *= u_units + y *= u_units + z *= u_units + else: + from .units import kelvin + + if u_units.has_same_units(kelvin): + x = u(f"{x} {units}") + y = u(f"{y} {units}") + z = u(f"{z} {units}") + else: + raise ValueError( + "You can't specify units that include temperature, " + "as this can't be mulitplied easily." + ) + + x = u(x) + y = u(y) + z = u(z) + + from .maths import Vector + + # find the units of the passed values + if x.is_zero() and y.is_zero() and z.is_zero(): + return Vector(0) + + units = None + + if not x.is_dimensionless(): + units = x.units() + + if not y.is_dimensionless(): + if units is None: + units = y.units() + elif not units.has_same_units(y): + raise ValueError( + "The passed y value has units that are aren't compatible with x. " + f"{x} versus {y}" + ) + + if not z.is_dimensionless(): + if units is None: + units = z.units() + elif not units.has_same_units(z): + raise ValueError( + "The passed z value has units that are aren't compatible with x or y. " + f"{x} versus {y} versus {z}" + ) + + if units is None: + # all dimensionless - will be a simple vector + return Vector(x.value(), y.value(), z.value()) + else: + if x.is_dimensionless(): + x = x * units + + if y.is_dimensionless(): + y = y * units + + if z.is_dimensionless(): + z = z * units + + # we have units - need to create a vector with the right type + from .units import angstrom + + if units.has_same_units(angstrom): + return Vector(x, y, z) + + from .units import picosecond + + if units.has_same_units(angstrom / picosecond): + from .legacy.Mol import Velocity3D + + return Velocity3D(x, y, z) + + from .units import newton + + if units.has_same_units(newton): + from .legacy.Mol import Force3D + + return Force3D(x, y, z) + + # no vector type for this - just return a simple vector + return (x, y, z) + + def molid( num: int = None, name: str = None, @@ -226,9 +363,7 @@ def molid( return ID -def atomid( - num: int = None, name: str = None, idx: int = None, case_sensitive=True -): +def atomid(num: int = None, name: str = None, idx: int = None, case_sensitive=True): """Construct an identifer for an Atom from the passed name, number and index. @@ -285,9 +420,7 @@ def atomid( return ID -def resid( - num: int = None, name: str = None, idx: int = None, case_sensitive=True -): +def resid(num: int = None, name: str = None, idx: int = None, case_sensitive=True): """Construct an identifer for a Residue from the passed name, number and index. diff --git a/src/sire/convert/CMakeLists.txt b/src/sire/convert/CMakeLists.txt index 6faea887c..5679b5784 100644 --- a/src/sire/convert/CMakeLists.txt +++ b/src/sire/convert/CMakeLists.txt @@ -11,3 +11,5 @@ set ( SCRIPTS # installation install( FILES ${SCRIPTS} DESTINATION ${SIRE_PYTHON}/sire/convert ) + +add_subdirectory (openmm) diff --git a/src/sire/convert/__init__.py b/src/sire/convert/__init__.py index 8ddd7aa25..243c512a6 100644 --- a/src/sire/convert/__init__.py +++ b/src/sire/convert/__init__.py @@ -19,6 +19,9 @@ _use_new_api() +# Importing the openmm sub-module so that it autocompletes when used +from . import openmm # noqa: F401 + def _to_selectormol(obj): from ..mol import SelectorMol diff --git a/src/sire/convert/openmm/CMakeLists.txt b/src/sire/convert/openmm/CMakeLists.txt new file mode 100644 index 000000000..f3d410d51 --- /dev/null +++ b/src/sire/convert/openmm/CMakeLists.txt @@ -0,0 +1,13 @@ +######################################## +# +# sire.convert.openmm +# +######################################## + +# Add your script to this list +set ( SCRIPTS + __init__.py + ) + +# installation +install( FILES ${SCRIPTS} DESTINATION ${SIRE_PYTHON}/sire/convert/openmm ) diff --git a/src/sire/convert/openmm/__init__.py b/src/sire/convert/openmm/__init__.py new file mode 100644 index 000000000..504225b33 --- /dev/null +++ b/src/sire/convert/openmm/__init__.py @@ -0,0 +1,8 @@ +__all__ = ["LambdaLever", "OpenMMMetaData", "PerturbableOpenMMMolecule", "SOMMContext"] + +from ...legacy.Convert import ( + LambdaLever, + PerturbableOpenMMMolecule, + OpenMMMetaData, + SOMMContext, +) diff --git a/src/sire/maths/__init__.py b/src/sire/maths/__init__.py index 410821a43..56f6c0e3d 100644 --- a/src/sire/maths/__init__.py +++ b/src/sire/maths/__init__.py @@ -48,9 +48,7 @@ align = _Maths.align -def rotate( - point, angle=None, axis=None, matrix=None, quaternion=None, center=None -): +def rotate(point, angle=None, axis=None, matrix=None, quaternion=None, center=None): """ Rotate the passed point by the passed angle and axis, or the passed matrix, or the passed quaternion, optionally centering @@ -90,9 +88,7 @@ def rotate( Returns: sire.maths.Vector The rotated vector """ - q = create_quaternion( - angle=angle, axis=axis, matrix=matrix, quaternion=quaternion - ) + q = create_quaternion(angle=angle, axis=axis, matrix=matrix, quaternion=quaternion) if center is None: center = Vector(0, 0, 0) @@ -222,7 +218,9 @@ def create_quaternion(angle=None, axis=None, matrix=None, quaternion=None): if not hasattr(EnergyTrajectory, "to_pandas"): - def _to_pandas(obj, temperature=None, to_alchemlyb: bool = False): + def _to_pandas( + obj, temperature=None, to_alchemlyb: bool = False, energy_unit: str = None + ): """ Return the energy trajectory as a pandas DataFrame @@ -241,18 +239,34 @@ def _to_pandas(obj, temperature=None, to_alchemlyb: bool = False): compatible with alchemlyb. This will allow the DataFrame to be used as part of an alchemlyb free energy calculation. + + energy_unit: str + Whichever of the alchemlyb energy units you want the output + DataFrame to use. This is in alchemlyb format, e.g. + `kcal/mol`, `kJ/mol`, or `kT`. This is only used if + `to_alchemlyb` is set to True. """ import pandas as pd - from ..units import picosecond, kcal_per_mol + from ..units import picosecond, kcal_per_mol, kJ_per_mol data = {} + convert_to_kt = False + if to_alchemlyb: time_unit = picosecond time_unit_string = "ps" - energy_unit = kcal_per_mol - energy_unit_string = "kcal/mol" + if energy_unit == "kT": + energy_unit = kcal_per_mol + energy_unit_string = "kT" + convert_to_kt = True + elif energy_unit == "kJ/mol": + energy_unit = kJ_per_mol + energy_unit_string = "kJ/mol" + else: + energy_unit = kcal_per_mol + energy_unit_string = "kcal/mol" if temperature is None: # look for the temperature in the ensemble property @@ -282,6 +296,11 @@ def _to_pandas(obj, temperature=None, to_alchemlyb: bool = False): keys = obj.label_keys() keys.sort() + if convert_to_kt: + from ..units import k_boltz + + kT = (k_boltz * temperature).to(kcal_per_mol) + for key in keys: if to_alchemlyb and key == "lambda": data["fep-lambda"] = obj.labels_as_numbers(key) @@ -311,7 +330,12 @@ def _to_pandas(obj, temperature=None, to_alchemlyb: bool = False): except Exception: column_header = key - data[column_header] = obj.energies(key, energy_unit) + nrgs = obj.energies(key, energy_unit) + + if convert_to_kt: + nrgs = [x / kT for x in nrgs] + + data[column_header] = nrgs if to_alchemlyb: df = pd.DataFrame(data).set_index(["time", "fep-lambda"]) @@ -329,7 +353,7 @@ def _to_pandas(obj, temperature=None, to_alchemlyb: bool = False): return df - def _to_alchemlyb(obj, temperature=None): + def _to_alchemlyb(obj, temperature=None, energy_unit: str = "kcal/mol"): """ Return the energy trajectory as an alchemlyb-formatted pandas DataFrame @@ -342,13 +366,20 @@ def _to_alchemlyb(obj, temperature=None): `ensemble` or `temperature` property will be used. + energy_unit: str + Whichever of the alchemlyb energy units you want the output + DataFrame to use. This is in alchemlyb format, e.g. + `kcal/mol`, `kJ/mol`, or `kT` + Returns ------- pandas.DataFrame A pandas DataFrame that is compatible with alchemlyb. """ - return obj.to_pandas(temperature=temperature, to_alchemlyb=True) + return obj.to_pandas( + temperature=temperature, to_alchemlyb=True, energy_unit=energy_unit + ) EnergyTrajectory.to_pandas = _to_pandas EnergyTrajectory.to_alchemlyb = _to_alchemlyb diff --git a/src/sire/maths/_vector.py b/src/sire/maths/_vector.py index f3a9f2d14..85c10aaf6 100644 --- a/src/sire/maths/_vector.py +++ b/src/sire/maths/_vector.py @@ -238,6 +238,7 @@ class containing 3 double precision values. These values def __init__(self, *args, **kwargs): from ..units import angstrom + from .. import u # get the default unit of length length_unit = angstrom.get_default() @@ -245,17 +246,24 @@ def __init__(self, *args, **kwargs): def _is_number(v): return isinstance(v, int) or isinstance(v, float) + def _is_str(v): + return isinstance(v, str) + # mix of doubles and lengths? new_args = [] for i in range(0, len(args)): if _is_number(args[i]): new_args.append((args[i] * length_unit).to(angstrom)) + elif _is_str(args[i]): + new_args.append(u(args[i]).to(angstrom)) elif hasattr(args[i], "__len__"): new_arg = [] for arg in args[i]: if _is_number(arg): new_arg.append((arg * length_unit).to(angstrom)) + elif _is_str(arg): + new_arg.append(u(arg).to(angstrom)) else: new_arg.append(arg.to(angstrom)) new_args.append(new_arg) @@ -265,6 +273,8 @@ def _is_number(v): for key in kwargs.keys(): if _is_number(kwargs[key]): kwargs[key] = (kwargs[key] * length_unit).to(angstrom) + elif _is_str(kwargs[key]): + kwargs[key] = u(kwargs[key]).to(angstrom) else: kwargs[key] = kwargs[key].to(angstrom) diff --git a/src/sire/mol/__init__.py b/src/sire/mol/__init__.py index a6420383f..2511b5045 100644 --- a/src/sire/mol/__init__.py +++ b/src/sire/mol/__init__.py @@ -226,9 +226,7 @@ def is_water(mol, map=None): if hasattr(type(mol), "molecules"): return _Mol.is_water(mol.molecules(), map) - raise TypeError( - f"Cannot convert {mol} to a molecule view or molecule collection." - ) + raise TypeError(f"Cannot convert {mol} to a molecule view or molecule collection.") # Here I will define some functions that make accessing @@ -280,9 +278,7 @@ def __is_atom_class(obj): def __is_residue_class(obj): mro = type(obj).mro() - return ( - Residue in mro or Selector_Residue_ in mro or SelectorM_Residue_ in mro - ) + return Residue in mro or Selector_Residue_ in mro or SelectorM_Residue_ in mro def __is_chain_class(obj): @@ -294,28 +290,19 @@ def __is_chain_class(obj): def __is_segment_class(obj): mro = type(obj).mro() - return ( - Segment in mro or Selector_Segment_ in mro or SelectorM_Segment_ in mro - ) + return Segment in mro or Selector_Segment_ in mro or SelectorM_Segment_ in mro def __is_cutgroup_class(obj): mro = type(obj).mro() - return ( - CutGroup in mro - or Selector_CutGroup_ in mro - or SelectorM_CutGroup_ in mro - ) + return CutGroup in mro or Selector_CutGroup_ in mro or SelectorM_CutGroup_ in mro def __is_selector_class(obj): try: t = obj.what() - return ( - t.find("SireMol::Selector") != -1 - or t.find("SireMM::Selector") != -1 - ) + return t.find("SireMol::Selector") != -1 or t.find("SireMM::Selector") != -1 except Exception: return False @@ -963,9 +950,7 @@ def __fixed__bond__(obj, idx=None, idx1=None, map=None): def __fixed__angle__(obj, idx=None, idx1=None, idx2=None, map=None): - angles = __fixed__angles__( - obj, idx, idx1, idx2, auto_reduce=False, map=map - ) + angles = __fixed__angles__(obj, idx, idx1, idx2, auto_reduce=False, map=map) if len(angles) == 0: raise KeyError("There is no matching angle in this view.") @@ -977,9 +962,7 @@ def __fixed__angle__(obj, idx=None, idx1=None, idx2=None, map=None): return angles[0] -def __fixed__dihedral__( - obj, idx=None, idx1=None, idx2=None, idx3=None, map=None -): +def __fixed__dihedral__(obj, idx=None, idx1=None, idx2=None, idx3=None, map=None): dihedrals = __fixed__dihedrals__( obj, idx, idx1, idx2, idx3, auto_reduce=False, map=map ) @@ -988,16 +971,13 @@ def __fixed__dihedral__( raise KeyError("There is no matching dihedral in this view.") elif len(dihedrals) > 1: raise KeyError( - "More than one dihedral matches. Number of " - f"matches is {len(dihedrals)}." + "More than one dihedral matches. Number of " f"matches is {len(dihedrals)}." ) return dihedrals[0] -def __fixed__improper__( - obj, idx=None, idx1=None, idx2=None, idx3=None, map=None -): +def __fixed__improper__(obj, idx=None, idx1=None, idx2=None, idx3=None, map=None): impropers = __fixed__impropers__( obj, idx, idx1, idx2, idx3, auto_reduce=False, map=map ) @@ -1006,8 +986,7 @@ def __fixed__improper__( raise KeyError("There is no matching improper in this view.") elif len(impropers) > 1: raise KeyError( - "More than one improper matches. Number of " - f"matches is {len(impropers)}." + "More than one improper matches. Number of " f"matches is {len(impropers)}." ) return impropers[0] @@ -1361,18 +1340,14 @@ def _apply(objs, func, *args, **kwargs): if str(func) == func: # we calling a named function - with ProgressBar( - total=len(objs), text="Looping through views" - ) as progress: + with ProgressBar(total=len(objs), text="Looping through views") as progress: for i, obj in enumerate(objs): result.append(getattr(obj, func)(*args, **kwargs)) progress.set_progress(i + 1) else: # we have been passed the function to call - with ProgressBar( - total=len(objs), text="Looping through views" - ) as progress: + with ProgressBar(total=len(objs), text="Looping through views") as progress: for i, obj in enumerate(objs): result.append(func(obj, *args, **kwargs)) progress.set_progress(i + 1) @@ -1594,12 +1569,16 @@ def _dynamics( pressure=None, vacuum=None, shift_delta=None, + shift_coulomb=None, coulomb_power=None, restraints=None, fixed=None, platform=None, device=None, precision=None, + com_reset_frequency=None, + barostat_frequency=None, + dynamic_constraints: bool = True, map=None, ): """ @@ -1712,11 +1691,16 @@ def _dynamics( simulation run in vacuum. shift_delta: length - The shift_delta parameter that controls the electrostatic - and van der Waals softening potential that smooths the + The shift_delta parameter that controls the Lennard-Jones + softening potential that smooths the creation and deletion of ghost atoms during a potential. This defaults to 2.0 A. + shift_coulomb: length + The shift_coulomb parameter that controls the electrostatic + softening potential that smooths the creation and deletion + of ghost atoms during a potential. This defaults to 1.0 A. + coulomb_power: int The coulomb power parmeter that controls the electrostatic softening potential that smooths the creation and deletion @@ -1745,6 +1729,24 @@ def _dynamics( The desired precision for the simulation (e.g. `single`, `mixed` or `double`) + com_reset_frequency: + Either the number of steps between center-of-mass resets, + or the time between resets. If this is unset, then + the center-of-mass is not reset during the simulation. + + barostat_frequency: + Either the number of steps between MC moves to apply the + barostat, of the time between moves. If this is unset, + then the default of every 25 steps is used. + + dynamic_constraints: bool + Whether or not to update the length of constraints of + perturbable bonds with lambda. This defaults to True, + meaning that changing lambda will change any constraint + on a perturbable bond to equal to the value of r0 at + that lambda value. If this is false, then the constraint + is set based on the current length. + map: dict A dictionary of additional options. Note that any options set in this dictionary that are also specified via one of @@ -1801,8 +1803,10 @@ def _dynamics( from ..units import femtosecond timestep = 1 * femtosecond - else: + elif timestep is not None: timestep = u(timestep) + else: + timestep = u(map["timestep"].value()) if save_frequency is None and not map.specified("save_frequency"): from ..units import picosecond @@ -1849,6 +1853,64 @@ def _dynamics( if integrator is not None: map.set("integrator", str(integrator)) + if dynamic_constraints is not None: + if dynamic_constraints: + map.set("dynamic_constraints", True) + else: + map.set("dynamic_constraints", False) + + if barostat_frequency is not None: + barostat_frequency = u(barostat_frequency) + elif map.specified("barostat_frequency"): + barostat_frequency = u(map["barostat_frequency"].value()) + + if barostat_frequency is None: + map.unset("barostat_frequency") + else: + if barostat_frequency.is_dimensionless(): + barostat_frequency = int(barostat_frequency.value()) + + if barostat_frequency != 0: + map.set("barostat_frequency", barostat_frequency) + else: + map.unset("barostat_frequency") + else: + if not barostat_frequency.has_same_units(timestep): + raise ValueError("The units of barostat_frequency must match timestep") + + barostat_frequency = int((barostat_frequency / timestep).value()) + + if barostat_frequency != 0: + map.set("barostat_frequency", barostat_frequency) + else: + map.unset("barostat_frequency") + + if com_reset_frequency is not None: + com_reset_frequency = u(com_reset_frequency) + elif map.specified("com_reset_frequency"): + com_reset_frequency = u(map["com_reset_frequency"].value()) + + if com_reset_frequency is None: + map.unset("com_reset_frequency") + else: + if com_reset_frequency.is_dimensionless(): + com_reset_frequency = int(com_reset_frequency.value()) + + if com_reset_frequency != 0: + map.set("com_reset_frequency", com_reset_frequency) + else: + map.unset("com_reset_frequency") + else: + if not com_reset_frequency.has_same_units(timestep): + raise ValueError("The units of com_reset_frequency must match timestep") + + com_reset_frequency = int((com_reset_frequency / timestep).value()) + + if com_reset_frequency != 0: + map.set("com_reset_frequency", com_reset_frequency) + else: + map.unset("com_reset_frequency") + return Dynamics( view, cutoff=cutoff, @@ -1859,6 +1921,7 @@ def _dynamics( schedule=schedule, lambda_value=lambda_value, shift_delta=shift_delta, + shift_coulomb=shift_coulomb, coulomb_power=coulomb_power, swap_end_states=swap_end_states, ignore_perturbations=ignore_perturbations, @@ -1881,12 +1944,14 @@ def _minimisation( ignore_perturbations=None, vacuum=None, shift_delta=None, + shift_coulomb=None, coulomb_power=None, platform=None, device=None, precision=None, restraints=None, fixed=None, + dynamic_constraints: bool = True, map=None, ): """ @@ -1954,11 +2019,16 @@ def _minimisation( simulation run in vacuum. shift_delta: length - The shift_delta parameter that controls the electrostatic - and van der Waals softening potential that smooths the + The shift_delta parameter that controls the Lennard-Jones + softening potential that smooths the creation and deletion of ghost atoms during a potential. This defaults to 2.0 A. + shift_coulomb: length + The shift_coulomb parameter that controls the electrostatic + softening potential that smooths the creation and deletion + of ghost atoms during a potential. This defaults to 1.0 A. + coulomb_power: int The coulomb power parmeter that controls the electrostatic softening potential that smooths the creation and deletion @@ -1987,6 +2057,14 @@ def _minimisation( The desired precision for the simulation (e.g. `single`, `mixed` or `double`) + dynamic_constraints: bool + Whether or not to update the length of constraints of + perturbable bonds with lambda. This defaults to True, + meaning that changing lambda will change any constraint + on a perturbable bond to equal to the value of r0 at + that lambda value. If this is false, then the constraint + is set based on the current length. + map: dict A dictionary of additional options. Note that any options set in this dictionary that are also specified via one of @@ -2053,6 +2131,12 @@ def _minimisation( if include_constrained_energies is not None: map.set("include_constrained_energies", include_constrained_energies) + if dynamic_constraints is not None: + if dynamic_constraints: + map.set("dynamic_constraints", True) + else: + map.set("dynamic_constraints", False) + if platform is not None: map.set("platform", str(platform)) @@ -2065,6 +2149,7 @@ def _minimisation( swap_end_states=swap_end_states, ignore_perturbations=ignore_perturbations, shift_delta=shift_delta, + shift_coulomb=shift_coulomb, coulomb_power=coulomb_power, restraints=restraints, fixed=fixed, @@ -2326,9 +2411,7 @@ def _total_energy(obj, other=None, map=None): elif other is None: return calculate_energy(mols.molecules(), map=map) else: - return calculate_energy( - mols.molecules(), _to_molecules(other), map=map - ) + return calculate_energy(mols.molecules(), _to_molecules(other), map=map) Atom.energy = _atom_energy @@ -2481,7 +2564,7 @@ def __mapping_map__(obj, atoms, container, match_all: bool = True): if not hasattr(Molecule, "perturbation"): - def __molecule_perturbation(mol): + def __molecule_perturbation(mol, map=None): """ Return an interface to the perturbable properties of this molecule. Note that the molecule must be @@ -2489,7 +2572,7 @@ def __molecule_perturbation(mol): """ from ..morph._perturbation import Perturbation - return Perturbation(mol) + return Perturbation(mol, map=map) def __molecule_is_perturbable(mol): """ diff --git a/src/sire/mol/_dynamics.py b/src/sire/mol/_dynamics.py index 575cd5108..7ac682867 100644 --- a/src/sire/mol/_dynamics.py +++ b/src/sire/mol/_dynamics.py @@ -94,6 +94,7 @@ def __init__(self, mols=None, map=None, **kwargs): self._elapsed_time = 0 * nanosecond self._walltime = 0 * nanosecond self._is_running = False + self._schedule_changed = False from ..convert import to @@ -235,7 +236,9 @@ def _exit_dynamics_block( if lambda_windows is not None: for lambda_value in lambda_windows: if lambda_value != sim_lambda_value: - self._omm_mols.set_lambda(lambda_value) + self._omm_mols.set_lambda( + lambda_value, update_constraints=False + ) nrgs[str(lambda_value)] = ( self._omm_mols.get_potential_energy( to_sire_units=False @@ -243,7 +246,7 @@ def _exit_dynamics_block( * kcal_per_mol ) - self._omm_mols.set_lambda(sim_lambda_value) + self._omm_mols.set_lambda(sim_lambda_value, update_constraints=False) self._energy_trajectory.set( self._current_time, nrgs, {"lambda": str(sim_lambda_value)} @@ -400,6 +403,17 @@ def perturbable_constraint(self): else: return self._omm_mols.get_perturbable_constraint() + def get_constraints(self): + """ + Return the actual list of constraints that have been applied + to this system. This is two lists of atoms, plus a list of + distances. The constraint is atom0[i]::atom1[i] with distance[i] + """ + if self.is_null(): + return None + else: + return self._omm_mols.get_constraints() + def get_schedule(self): if self.is_null(): return None @@ -409,6 +423,8 @@ def get_schedule(self): def set_schedule(self, schedule): if not self.is_null(): self._omm_mols.set_lambda_schedule(schedule) + self._schedule_changed = True + self.set_lambda(self._omm_mols.get_lambda()) def get_lambda(self): if self.is_null(): @@ -416,7 +432,7 @@ def get_lambda(self): else: return self._omm_mols.get_lambda() - def set_lambda(self, lambda_value: float): + def set_lambda(self, lambda_value: float, update_constraints: bool = True): if not self.is_null(): s = self.get_schedule() @@ -425,11 +441,16 @@ def set_lambda(self, lambda_value: float): lambda_value = s.clamp(lambda_value) - if lambda_value == self._omm_mols.get_lambda(): + if (not self._schedule_changed) and ( + lambda_value == self._omm_mols.get_lambda() + ): # nothing to do return - self._omm_mols.set_lambda(lambda_value) + self._omm_mols.set_lambda( + lambda_value=lambda_value, update_constraints=update_constraints + ) + self._schedule_changed = False self._clear_state() def integrator(self): @@ -947,6 +968,7 @@ def __init__( swap_end_states=None, ignore_perturbations=None, shift_delta=None, + shift_coulomb=None, coulomb_power=None, restraints=None, fixed=None, @@ -972,6 +994,9 @@ def __init__( if shift_delta is not None: _add_extra(extras, "shift_delta", u(shift_delta)) + if shift_coulomb is not None: + _add_extra(extras, "shift_coulomb", u(shift_coulomb)) + _add_extra(extras, "coulomb_power", coulomb_power) _add_extra(extras, "restraints", restraints) _add_extra(extras, "fixed", fixed) @@ -1155,14 +1180,22 @@ def get_lambda(self): """ return self._d.get_lambda() - def set_lambda(self, lambda_value: float): + def set_lambda(self, lambda_value: float, update_constraints: bool = True): """ Set the current value of lambda for this system. This will update the forcefield parameters in the context according to the data in the LambdaSchedule. This does nothing if - this isn't a perturbable system + this isn't a perturbable system. + + If `update_constraints` is True, then this will also update + the constraint length of any constrained perturbable bonds. + These will be set to the r0 value for that bond at this + value of lambda. If `update_constraints` is False, then + the constraint will not be changed. """ - self._d.set_lambda(lambda_value) + self._d.set_lambda( + lambda_value=lambda_value, update_constraints=update_constraints + ) def ensemble(self): """ @@ -1229,6 +1262,14 @@ def perturbable_constraint(self): """ return self._d.perturbable_constraint() + def get_constraints(self): + """ + Return the actual list of constraints that have been applied + to this system. This is two lists of atoms, plus a list of + distances. The constraint is atom0[i]::atom1[i] with distance[i] + """ + return self._d.get_constraints() + def integrator(self): """ Return the integrator that is used to run dynamics @@ -1356,13 +1397,13 @@ def current_potential_energy(self, lambda_values=None): try: for lambda_value in lambda_values: - self.set_lambda(lambda_value) + self.set_lambda(lambda_value, update_constraints=False) nrgs.append(self._d.current_potential_energy()) except Exception: - self.set_lambda(old_lambda) + self.set_lambda(old_lambda, update_constraints=False) raise - self.set_lambda(old_lambda) + self.set_lambda(old_lambda, update_constraints=False) return nrgs diff --git a/src/sire/mol/_minimisation.py b/src/sire/mol/_minimisation.py index 280403aa6..92dfcc45f 100644 --- a/src/sire/mol/_minimisation.py +++ b/src/sire/mol/_minimisation.py @@ -20,12 +20,14 @@ def __init__( swap_end_states=None, ignore_perturbations=None, shift_delta=None, + shift_coulomb=None, coulomb_power=None, restraints=None, fixed=None, ): from ..base import create_map from ._dynamics import DynamicsData, _add_extra + from .. import u extras = {} @@ -35,7 +37,13 @@ def __init__( _add_extra(extras, "lambda", lambda_value) _add_extra(extras, "swap_end_states", swap_end_states) _add_extra(extras, "ignore_perturbations", ignore_perturbations) - _add_extra(extras, "shift_delta", shift_delta) + + if shift_delta is not None: + _add_extra(extras, "shift_delta", u(shift_delta)) + + if shift_coulomb is not None: + _add_extra(extras, "shift_coulomb", u(shift_coulomb)) + _add_extra(extras, "coulomb_power", coulomb_power) _add_extra(extras, "restraints", restraints) _add_extra(extras, "fixed", fixed) @@ -50,6 +58,28 @@ def __str__(self): def __repr__(self): return self.__str__() + def constraint(self): + """ + Return the constraint used for the minimisation (e.g. constraining + bonds involving hydrogens etc.) + """ + return self._d.constraint() + + def perturbable_constraint(self): + """ + Return the perturbable constraint used for the minimisation (e.g. + constraining bonds involving hydrogens etc.) + """ + return self._d.perturbable_constraint() + + def get_constraints(self): + """ + Return the actual list of constraints that have been applied + to this system. This is two lists of atoms, plus a list of + distances. The constraint is atom0[i]::atom1[i] with distance[i] + """ + return self._d.get_constraints() + def run(self, max_iterations: int = 10000): """ Perform minimisation on the molecules, running a maximum diff --git a/src/sire/mol/_view.py b/src/sire/mol/_view.py index 2a668d373..1111153a7 100644 --- a/src/sire/mol/_view.py +++ b/src/sire/mol/_view.py @@ -327,6 +327,7 @@ def view( smooth=False, wrap=True, mapping=None, + bgcolor=None, stage_parameters: str = None, map=None, ): @@ -406,6 +407,10 @@ def view( Set the selection strings for atoms that should be represented with these views. + bgcolor: str + The background color of the view. Set to 'None' to use + the default background color (black) + stage_parameters: dict An optional dictionary that will be passed directly to the NGLView object to set the stage parameters. @@ -607,15 +612,26 @@ def _check(rep): p1 = p.start("stage parameters") if stage_parameters is None: + if bgcolor is None: + bgcolor = "black" + else: + bgcolor = str(bgcolor).lower() + view.stage.set_parameters( clipNear=0, clipFar=100, clipDist=0, fogNear=100, fogFar=1000, - backgroundColor="black", + backgroundColor=bgcolor, ) else: + if bgcolor is not None: + from copy import deepcopy + + stage_parameters = deepcopy(stage_parameters) + stage_parameters["backgroundColor"] = str(bgcolor).lower() + view.stage.set_parameters(**stage_parameters) if (rest is None) or (rest is False): diff --git a/src/sire/morph/CMakeLists.txt b/src/sire/morph/CMakeLists.txt index 0cc85b497..d0ce6a852 100644 --- a/src/sire/morph/CMakeLists.txt +++ b/src/sire/morph/CMakeLists.txt @@ -10,6 +10,7 @@ set ( SCRIPTS _alchemy.py _ghost_atoms.py _hmr.py + _pertfile.py _perturbation.py _repex.py ) diff --git a/src/sire/morph/__init__.py b/src/sire/morph/__init__.py index 1a30c86d5..d0b26373d 100644 --- a/src/sire/morph/__init__.py +++ b/src/sire/morph/__init__.py @@ -3,10 +3,27 @@ "replica_exchange", "repartition_hydrogen_masses", "to_alchemlyb", + "create_from_pertfile", + "extract_reference", + "extract_perturbed", + "link_to_reference", + "link_to_perturbed", + "zero_ghost_bonds", + "zero_ghost_angles", + "zero_ghost_torsions", "Perturbation", ] -from ._perturbation import Perturbation +from ._perturbation import ( + Perturbation, + link_to_reference, + link_to_perturbed, + extract_reference, + extract_perturbed, + zero_ghost_bonds, + zero_ghost_angles, + zero_ghost_torsions, +) from ._ghost_atoms import shrink_ghost_atoms @@ -15,3 +32,5 @@ from ._hmr import repartition_hydrogen_masses from ._alchemy import to_alchemlyb + +from ._pertfile import create_from_pertfile diff --git a/src/sire/morph/_alchemy.py b/src/sire/morph/_alchemy.py index f895212ac..b66689524 100644 --- a/src/sire/morph/_alchemy.py +++ b/src/sire/morph/_alchemy.py @@ -1,7 +1,7 @@ __all__ = ["to_alchemlyb"] -def to_alchemlyb(energy_trajectories, temperature=None): +def to_alchemlyb(energy_trajectories, temperature=None, energy_unit="kcal/mol"): """ Convert the passed list of energy trajectories into a single alchemlyb-compatible DataFrame, ready for @@ -22,6 +22,11 @@ def to_alchemlyb(energy_trajectories, temperature=None): the temperature will be taken from the values in each EnergyTrajectory + energy_unit: str + Whichever of the alchemlyb energy units you want the output + DataFrame to use. This is in alchemlyb format, e.g. + `kcal/mol`, `kJ/mol`, or `kT` + Returns ------- @@ -48,7 +53,9 @@ def to_alchemlyb(energy_trajectories, temperature=None): energy_trajectory = load(energy_trajectory) - df = energy_trajectory.to_alchemlyb(temperature=temperature) + df = energy_trajectory.to_alchemlyb( + temperature=temperature, energy_unit=energy_unit + ) # get the lambda value of the first row try: @@ -75,7 +82,17 @@ def to_alchemlyb(energy_trajectories, temperature=None): for lambda_value in lambda_values: dfs.extend(dataframes[lambda_value]) - # now concatenate the dataframes - import pandas as pd + if len(dfs) == 0: + return None + elif len(dfs) == 1: + return dfs[0] + else: + attrs = dfs[0].attrs + + # now concatenate the dataframes + import pandas as pd + + combined = pd.concat(dfs) + combined.attrs = attrs - return pd.concat(dfs) + return combined diff --git a/src/sire/morph/_ghost_atoms.py b/src/sire/morph/_ghost_atoms.py index 4a71728ab..bb1955309 100644 --- a/src/sire/morph/_ghost_atoms.py +++ b/src/sire/morph/_ghost_atoms.py @@ -13,6 +13,8 @@ def shrink_ghost_atoms(mols, length=None, map=None): from ..cas import Symbol from ..mm import AmberBond + mols = mols.clone() + if length is None: length = 0.6 else: @@ -64,13 +66,10 @@ def shrink_ghost_atoms(mols, length=None, map=None): for bond in mol.bonds(): # are either of the atoms in the bond in from_ghosts or to_ghosts is_from_ghost = ( - bond[0].index() in from_ghosts - or bond[1].index() in from_ghosts + bond[0].index() in from_ghosts or bond[1].index() in from_ghosts ) - is_to_ghost = ( - bond[0].index() in to_ghosts or bond[1].index() in to_ghosts - ) + is_to_ghost = bond[0].index() in to_ghosts or bond[1].index() in to_ghosts if is_from_ghost and not is_to_ghost: # this is a bond that is turning into a ghost @@ -97,14 +96,12 @@ def shrink_ghost_atoms(mols, length=None, map=None): changed1 = True if changed0: - mol = ( - mol.edit().set_property(map0["bond"].source(), bonds0).commit() - ) + mol = mol.edit().set_property(map0["bond"].source(), bonds0).commit() if changed1: - mol = ( - mol.edit().set_property(map1["bond"].source(), bonds1).commit() - ) + mol = mol.edit().set_property(map1["bond"].source(), bonds1).commit() if changed0 or changed1: mols.update(mol) + + return mols diff --git a/src/sire/morph/_pertfile.py b/src/sire/morph/_pertfile.py new file mode 100644 index 000000000..5ac77bb08 --- /dev/null +++ b/src/sire/morph/_pertfile.py @@ -0,0 +1,306 @@ +__all__ = ["create_from_pertfile"] + + +def create_from_pertfile(mol, pertfile, map=None): + """ + Create a merged molecule from the passed molecule and pertfile. + This will create and return a merged molecule with an initial + and reference state that follows the instructions in the + pertfile. + + Parameters + ---------- + + mol : sire.mol.Molecule + The molecule to be merged + + pertfile : str + The path to the pertfile + + Returns + ------- + + sire.mol.Molecule + The merged molecule + """ + from ..legacy.IO import PerturbationsLibrary + + from ..base import create_map + + map = create_map(map) + + # we operate on the whole molecule + mol = mol.molecule() + + # find the name of the molecule in the pertfile... + molname = None + + for line in open(pertfile).readlines(): + if line.startswith("molecule"): + try: + molname = line.split()[1] + break + except IndexError: + pass + + if molname is None: + raise ValueError(f"Could not find molecule name in pertfile: {pertfile}") + + perturbations = PerturbationsLibrary(pertfile) + + # get the template for the first molecule in this file + # (there only ever seems to be a single template per file) + template = perturbations.get_template(molname) + + # The pert file identifies using atom names - create a map to + # identify atoms by index + name_to_idx = {} + + for atom in mol.atoms(): + name_to_idx[atom.name().value()] = atom.index().value() + + # update all of the atoms from the data in the pertfile + c = mol.cursor() + + chg_prop = map["charge"].source() + lj_prop = map["LJ"].source() + typ_prop = map["ambertype"].source() + + c["charge0"] = c[chg_prop] + c["charge1"] = c[chg_prop] + + c["LJ0"] = c[lj_prop] + c["LJ1"] = c[lj_prop] + + c["ambertype0"] = c[typ_prop] + c["ambertype1"] = c[typ_prop] + + for atom in c.atoms(): + atomname = atom.name + + try: + q0 = template.get_init_charge(atomname) + q1 = template.get_final_charge(atomname) + lj0 = template.get_init_lj(atomname) + lj1 = template.get_final_lj(atomname) + typ0 = template.get_init_type(atomname) + typ1 = template.get_final_type(atomname) + except Exception as e: + print(e) + next + + atom["charge0"] = q0 + atom["charge1"] = q1 + + atom["LJ0"] = lj0 + atom["LJ1"] = lj1 + + atom["ambertype0"] = typ0 + atom["ambertype1"] = typ1 + + # now update all of the internals + bond_prop = map["bond"].source() + ang_prop = map["angle"].source() + dih_prop = map["dihedral"].source() + imp_prop = map["improper"].source() + + bonds0 = mol.property(bond_prop) + bonds1 = bonds0.clone() + + angles0 = mol.property(ang_prop) + angles1 = angles0.clone() + + dihedrals0 = mol.property(dih_prop) + dihedrals1 = dihedrals0.clone() + + impropers0 = mol.property(imp_prop) + impropers1 = impropers0.clone() + + from ..legacy.MM import AmberBond, AmberAngle, AmberDihedral, AmberDihPart + from ..cas import Symbol + from ..mol import AtomIdx, BondID, AngleID, DihedralID, ImproperID + + r = Symbol("r") + theta = Symbol("theta") + phi = Symbol("phi") + + for bond in template.get_bonds(): + atom1 = bond.atom0() + atom2 = bond.atom1() + + atom1_idx = AtomIdx(name_to_idx[atom1.value()]) + atom2_idx = AtomIdx(name_to_idx[atom2.value()]) + + k0 = template.get_init_bond_k(bond) + k1 = template.get_final_bond_k(bond) + + r0 = template.get_init_bond_r(bond) + r1 = template.get_final_bond_r(bond) + + bonds0.set( + BondID(atom1_idx, atom2_idx), + AmberBond(k0, r0).to_expression(r), + ) + + bonds1.set( + BondID(atom1_idx, atom2_idx), + AmberBond(k1, r1).to_expression(r), + ) + + c["bond0"] = bonds0 + c["bond1"] = bonds1 + + for angle in template.get_angles(): + atom1 = angle.atom0() + atom2 = angle.atom1() + atom3 = angle.atom2() + + atom1_idx = AtomIdx(name_to_idx[atom1.value()]) + atom2_idx = AtomIdx(name_to_idx[atom2.value()]) + atom3_idx = AtomIdx(name_to_idx[atom3.value()]) + + k0 = template.get_init_angle_k(angle) + k1 = template.get_final_angle_k(angle) + + theta0 = template.get_init_angle_t(angle) + theta1 = template.get_final_angle_t(angle) + + angles0.set( + AngleID(atom1_idx, atom2_idx, atom3_idx), + AmberAngle(k0, theta0).to_expression(theta), + ) + + angles1.set( + AngleID(atom1_idx, atom2_idx, atom3_idx), + AmberAngle(k1, theta1).to_expression(theta), + ) + + c["angle0"] = angles0 + c["angle1"] = angles1 + + for dihedral in template.get_dihedrals(): + atom1 = dihedral.atom0() + atom2 = dihedral.atom1() + atom3 = dihedral.atom2() + atom4 = dihedral.atom3() + + atom1_idx = AtomIdx(name_to_idx[atom1.value()]) + atom2_idx = AtomIdx(name_to_idx[atom2.value()]) + atom3_idx = AtomIdx(name_to_idx[atom3.value()]) + atom4_idx = AtomIdx(name_to_idx[atom4.value()]) + + params0 = template.get_init_dih_params(dihedral) + params1 = template.get_final_dih_params(dihedral) + + func0 = 0 + func1 = 0 + + for i in range(0, len(params0), 3): + k0 = params0[i] + n0 = params0[i + 1] + phi0 = params0[i + 2] + + func0 += AmberDihedral(AmberDihPart(k0, n0, phi0)).to_expression(phi) + + for i in range(0, len(params1), 3): + k1 = params1[i] + n1 = params1[i + 1] + phi1 = params1[i + 2] + + func1 += AmberDihedral(AmberDihPart(k1, n1, phi1)).to_expression(phi) + + dihedrals0.set(DihedralID(atom1_idx, atom2_idx, atom3_idx, atom4_idx), func0) + dihedrals1.set(DihedralID(atom1_idx, atom2_idx, atom3_idx, atom4_idx), func1) + + c["dihedral0"] = dihedrals0 + c["dihedral1"] = dihedrals1 + + for improper in template.get_impropers(): + atom1 = improper.atom0() + atom2 = improper.atom1() + atom3 = improper.atom2() + atom4 = improper.atom3() + + atom1_idx = AtomIdx(name_to_idx[atom1.value()]) + atom2_idx = AtomIdx(name_to_idx[atom2.value()]) + atom3_idx = AtomIdx(name_to_idx[atom3.value()]) + atom4_idx = AtomIdx(name_to_idx[atom4.value()]) + + params0 = template.get_init_imp_params(improper) + params1 = template.get_final_imp_params(improper) + + func0 = 0 + func1 = 0 + + for i in range(0, len(params0), 3): + k0 = params0[i] + n0 = params0[i + 1] + phi0 = params0[i + 2] + + func0 += AmberDihedral(AmberDihPart(k0, n0, phi0)).to_expression(phi) + + for i in range(0, len(params1), 3): + k1 = params1[i] + n1 = params1[i + 1] + phi1 = params1[i + 2] + + func1 += AmberDihedral(AmberDihPart(k1, n1, phi1)).to_expression(phi) + + impropers0.set(ImproperID(atom1_idx, atom2_idx, atom3_idx, atom4_idx), func0) + impropers1.set(ImproperID(atom1_idx, atom2_idx, atom3_idx, atom4_idx), func1) + + c["improper0"] = impropers0 + c["improper1"] = impropers1 + + # duplicate the coordinates, mass, and element properties + for prop in ["coordinates", "mass", "element", "forcefield", "intrascale"]: + orig_prop = map[prop].source() + c[prop + "0"] = c[orig_prop] + c[prop + "1"] = c[orig_prop] + del c[orig_prop] + + # now remove all of the "default" properties + del c[chg_prop] + del c[lj_prop] + del c[typ_prop] + del c[bond_prop] + del c[ang_prop] + del c[dih_prop] + del c[imp_prop] + + mol = c.commit() + + # we now need to generat the AmberParameters for the two end states + from ..legacy.MM import AmberParams + + map0 = { + "charge": "charge0", + "LJ": "LJ0", + "ambertype": "ambertype0", + "bond": "bond0", + "angle": "angle0", + "dihedral": "dihedral0", + "improper": "improper0", + } + + params0 = AmberParams(mol, map0) + + map1 = { + "charge": "charge1", + "LJ": "LJ1", + "ambertype": "ambertype1", + "bond": "bond1", + "angle": "angle1", + "dihedral": "dihedral1", + "improper": "improper1", + } + + params1 = AmberParams(mol, map1) + + c["parameters0"] = params0 + c["parameters1"] = params1 + + c["is_perturbable"] = True + + # make sure that we link to the reference state + return c.commit().perturbation().link_to_reference(auto_commit=True) diff --git a/src/sire/morph/_perturbation.py b/src/sire/morph/_perturbation.py index bfe3ec765..3cb36a9ba 100644 --- a/src/sire/morph/_perturbation.py +++ b/src/sire/morph/_perturbation.py @@ -1,4 +1,110 @@ -__all__ = ["Perturbation"] +__all__ = [ + "Perturbation", + "link_to_reference", + "link_to_perturbed", + "extract_reference", + "extract_perturbed", + "zero_ghost_bonds", + "zero_ghost_angles", + "zero_ghost_torsions", +] + + +def link_to_reference(mols, map=None): + """ + Return the passed molecule(s), where any perturbable molecules + are linked to their reference (lambda=0) states + """ + mols = mols.clone() + + for mol in mols.molecules("property is_perturbable"): + mols.update(mol.perturbation(map=map).link_to_reference(auto_commit=True)) + + return mols + + +def link_to_perturbed(mols, map=None): + """ + Return the passed molecule(s), where any perturbable molecules + are linked to their perturbed (lambda=1) states + """ + mols = mols.clone() + + for mol in mols.molecules("property is_perturbable"): + mols.update(mol.perturbation(map=map).link_to_perturbed(auto_commit=True)) + + return mols + + +def extract_reference(mols, remove_ghosts: bool = True, map=None): + """ + Return the passed molecule(s) where any perturbable molecules + have been extracted to their reference (lambda=0) state (i.e. deleting + their perturbed state) + """ + mols = mols.clone() + + for mol in mols.molecules("property is_perturbable"): + mols.update( + mol.perturbation(map=map).extract_reference(remove_ghosts=remove_ghosts) + ) + + return mols + + +def extract_perturbed(mols, remove_ghosts: bool = True, map=None): + """ + Return the passed molecule(s) where any perturbable molecules + have been extracted to their perturbed (lambda=1) state (i.e. deleting + their reference state) + """ + mols = mols.clone() + + for mol in mols.molecules("property is_perturbable"): + mols.update( + mol.perturbation(map=map).extract_perturbed(remove_ghosts=remove_ghosts) + ) + + return mols + + +def zero_ghost_bonds(mols, map=None): + """ + Zero any bonds in the reference or perturbed + states where any of the atoms in those states is a ghost (dummy) atom + """ + mols = mols.clone() + + for mol in mols.molecules("property is_perturbable"): + mols.update(mol.perturbation(map=map).zero_ghost_bonds(auto_commit=True)) + + return mols + + +def zero_ghost_angles(mols, map=None): + """ + Zero any angles in the reference or perturbed + states where any of the atoms in those states is a ghost (dummy) atom + """ + mols = mols.clone() + + for mol in mols.molecules("property is_perturbable"): + mols.update(mol.perturbation(map=map).zero_ghost_angles(auto_commit=True)) + + return mols + + +def zero_ghost_torsions(mols, map=None): + """ + Zero any torsions (dihedrals or impropers) in the reference or perturbed + states where any of the atoms in those states is a ghost (dummy) atom + """ + mols = mols.clone() + + for mol in mols.molecules("property is_perturbable"): + mols.update(mol.perturbation(map=map).zero_ghost_torsions(auto_commit=True)) + + return mols class Perturbation: @@ -18,14 +124,12 @@ def __init__(self, mol, map=None): if not mol.has_property(map["is_perturbable"]): raise ValueError( - "You can only create a `Perturbation` from a " - "perturbable molecule!" + "You can only create a `Perturbation` from a " "perturbable molecule!" ) if not mol.property(map["is_perturbable"]): raise ValueError( - "You can only create a `Perturbation` from a " - "perturbable molecule!" + "You can only create a `Perturbation` from a " "perturbable molecule!" ) # construct the perturbation objects that can move the @@ -63,6 +167,7 @@ def __init__(self, mol, map=None): "treechain", ] + self._map = map self._map0 = map.add_suffix("0", props) self._map1 = map.add_suffix("1", props) @@ -139,7 +244,121 @@ def __str__(self): def __repr__(self): return self.__str__() - def link_to_reference(self, properties: list[str] = None): + def extract_reference( + self, + properties: list[str] = None, + remove_ghosts: bool = True, + ): + """ + Extract the reference state properties of the molecule + and remove the perturbed state. + + If a list of properties is passed then only those properties will + be extracted to the reference molecule + + Parameters + ---------- + + properties: list[str] + The list of properties to extract to the reference molecule + + remove_ghosts: bool + If True then any ghost atoms will be removed from the molecule + """ + if properties is None: + properties = [] + + for key in self._mol.property_keys(): + if key.endswith("0"): + properties.append(key[:-1]) + + elif type(properties) is str: + properties = [properties] + + mol = self._mol.molecule().edit() + + for key in properties: + ref_prop = f"{key}0" + pert_prop = f"{key}1" + + if mol.has_property(ref_prop): + if mol.has_property(key): + mol.remove_property(key) + + mol.set_property(key, mol.property(ref_prop)) + mol.remove_property(ref_prop) + + if mol.has_property(pert_prop): + mol.remove_property(pert_prop) + + if mol.has_property("is_perturbable"): + mol.remove_property("is_perturbable") + + mol = mol.commit().molecule() + + if remove_ghosts: + mol = mol["not element Xx"].extract(to_same_molecule=True) + + return mol + + def extract_perturbed( + self, + properties: list[str] = None, + remove_ghosts: bool = True, + ): + """ + Extract the perturbed state properties of the molecule + and remove the reference state. + + If a list of properties is passed then only those properties will + be extracted to the reference molecule + + Parameters + ---------- + + properties: list[str] + The list of properties to extract to the perturbed molecule + + remove_ghosts: bool + If True then any ghost atoms will be removed from the molecule + """ + if properties is None: + properties = [] + + for key in self._mol.property_keys(): + if key.endswith("1"): + properties.append(key[:-1]) + + elif type(properties) is str: + properties = [properties] + + mol = self._mol.molecule().edit() + + for key in properties: + ref_prop = f"{key}0" + pert_prop = f"{key}1" + + if mol.has_property(pert_prop): + if mol.has_property(key): + mol.remove_property(key) + + mol.set_property(key, mol.property(pert_prop)) + mol.remove_property(pert_prop) + + if mol.has_property(ref_prop): + mol.remove_property(ref_prop) + + if mol.has_property("is_perturbable"): + mol.remove_property("is_perturbable") + + mol = mol.commit().molecule() + + if remove_ghosts: + mol = mol["not element Xx"].extract(to_same_molecule=True) + + return mol + + def link_to_reference(self, properties: list[str] = None, auto_commit: bool = True): """ Link all of the properties of the molecule to their values in the reference molecule (lambda=0). @@ -152,6 +371,9 @@ def link_to_reference(self, properties: list[str] = None): properties: list[str] The list of properties to link to the reference molecule + + auto_commit: bool + If True then the molecule will be committed and returned """ if properties is None: properties = [] @@ -172,9 +394,13 @@ def link_to_reference(self, properties: list[str] = None): mol.add_link(key, f"{key}0") self._mol.update(mol.commit()) - return self - def link_to_perturbed(self, properties: list[str] = None): + if auto_commit: + return self.commit() + else: + return self + + def link_to_perturbed(self, properties: list[str] = None, auto_commit: bool = True): """ Link all of the properties of the molecule to their values in the perturbed molecule (lambda=1). @@ -187,6 +413,9 @@ def link_to_perturbed(self, properties: list[str] = None): properties: list[str] The list of properties to link to the perturbed molecule + + auto_commit: bool + If True then the molecule will be committed and returned """ if properties is None: properties = [] @@ -207,7 +436,11 @@ def link_to_perturbed(self, properties: list[str] = None): mol.add_link(key, f"{key}1") self._mol.update(mol.commit()) - return self + + if auto_commit: + return self.commit() + else: + return self def set_lambda(self, lam_val: float): """ @@ -247,7 +480,7 @@ def commit(self): """ return self._mol.clone() - def view(self, *args, **kwargs): + def view(self, *args, state="perturbed", **kwargs): """ View the perturbation """ @@ -257,7 +490,11 @@ def view(self, *args, **kwargs): map = create_map({}) - mol = self._mol.clone() + if str(state).lower() == "perturbed": + mol = self._mol.clone().perturbation(map=self._map).link_to_perturbed() + else: + mol = self._mol.clone().perturbation(map=self._map).link_to_reference() + mol.delete_all_frames(map=map) if mol.is_link(map["coordinates"]): @@ -265,10 +502,7 @@ def view(self, *args, **kwargs): # as we will be replacing it with the calculated perturbed # coordinates mol.update( - mol.molecule() - .edit() - .remove_link(map["coordinates"].source()) - .commit() + mol.molecule().edit().remove_link(map["coordinates"].source()).commit() ) if not mol.has_property(map["coordinates"]): @@ -292,4 +526,293 @@ def view(self, *args, **kwargs): mol = self._perturbations.perturb(mol, vals) mol.save_frame() - return mol.view(*args, **kwargs) + return mol["not element Xx"].view(*args, **kwargs) + + def view_reference(self, *args, **kwargs): + """ + View the reference state of the perturbation + """ + return self.view(*args, state="reference", **kwargs) + + def view_perturbed(self, *args, **kwargs): + """ + View the perturbed state of the perturbation + """ + return self.view(*args, state="perturbed", **kwargs) + + def zero_ghost_bonds(self, auto_commit: bool = True): + """ + Zero the bonds in the reference and perturbed states where any of + the atoms in those states is a ghost atom + """ + p = self.to_openmm() + + zero_in_ref = [] + zero_in_pert = [] + + from_ghosts = [] + to_ghosts = [] + + atoms = p.atoms() + + for idx in p.get_to_ghost_idxs(): + to_ghosts.append(atoms[idx].index()) + + for idx in p.get_from_ghost_idxs(): + from_ghosts.append(atoms[idx].index()) + + for bond in p.bonds(): + atoms = bond.atoms() + + n_in_from = sum([int(atoms[i].index() in from_ghosts) for i in range(2)]) + n_in_to = sum([int(atoms[i].index() in to_ghosts) for i in range(2)]) + + if n_in_from == 0 and n_in_to == 0: + continue + elif n_in_from == 0: + zero_in_pert.append(bond) + elif n_in_to == 0: + zero_in_ref.append(bond) + else: + zero_in_pert.append(bond) + zero_in_ref.append(bond) + + if len(zero_in_ref) == 0 and len(zero_in_pert) == 0: + # nothing to do + return self + + mol = self._mol.molecule().edit() + + if len(zero_in_ref) > 0: + bonds = self._mol.property(self._map["bond0"]) + + for bond in zero_in_ref: + bonds.clear(bond.id()) + + mol.set_property(self._map["bond0"].source(), bonds) + + if len(zero_in_pert) > 0: + bonds = self._mol.property(self._map["bond1"]) + + for bond in zero_in_pert: + bonds.clear(bond.id()) + + mol.set_property(self._map["bond1"].source(), bonds) + + self._mol.update(mol.commit()) + + if auto_commit: + return self.commit() + else: + return self + + def zero_ghost_angles(self, auto_commit: bool = True): + """ + Zero the angles in the reference and perturbed states where any of + the atoms in those states is a ghost atom + """ + p = self.to_openmm() + + zero_in_ref = [] + zero_in_pert = [] + + from_ghosts = [] + to_ghosts = [] + + atoms = p.atoms() + + for idx in p.get_to_ghost_idxs(): + to_ghosts.append(atoms[idx].index()) + + for idx in p.get_from_ghost_idxs(): + from_ghosts.append(atoms[idx].index()) + + for angle in p.angles(): + atoms = angle.atoms() + + n_in_from = sum([int(atoms[i].index() in from_ghosts) for i in range(3)]) + n_in_to = sum([int(atoms[i].index() in to_ghosts) for i in range(3)]) + + if n_in_from == 0 and n_in_to == 0: + continue + elif n_in_from == 0: + # don't zero if all are ghosts + if n_in_to < 3: + zero_in_pert.append(angle) + elif n_in_to == 0: + # don't zero if all are ghosts + if n_in_from < 3: + zero_in_ref.append(angle) + else: + zero_in_pert.append(angle) + zero_in_ref.append(angle) + + if len(zero_in_ref) == 0 and len(zero_in_pert) == 0: + # nothing to do + return self + + mol = self._mol.molecule().edit() + + if len(zero_in_ref) > 0: + angs = self._mol.property(self._map["angle0"]) + + for angle in zero_in_ref: + angs.clear(angle.id()) + + mol.set_property(self._map["angle0"].source(), angs) + + if len(zero_in_pert) > 0: + angs = self._mol.property(self._map["angle1"]) + + for angle in zero_in_pert: + angs.clear(angle.id()) + + mol.set_property(self._map["angle1"].source(), angs) + + self._mol.update(mol.commit()) + + if auto_commit: + return self.commit() + else: + return self + + def zero_ghost_torsions(self, auto_commit: bool = True): + """ + Zero the torsions (dihedrals and impropers) in the reference and + perturbed states where any of the atoms in those states is a ghost atom + """ + p = self.to_openmm() + + zero_in_ref = [] + zero_in_pert = [] + + from_ghosts = [] + to_ghosts = [] + + atoms = p.atoms() + + for idx in p.get_to_ghost_idxs(): + to_ghosts.append(atoms[idx].index()) + + for idx in p.get_from_ghost_idxs(): + from_ghosts.append(atoms[idx].index()) + + for torsion in p.torsions(): + atoms = torsion.atoms() + + n_in_from = sum([int(atoms[i].index() in from_ghosts) for i in range(4)]) + n_in_to = sum([int(atoms[i].index() in to_ghosts) for i in range(4)]) + + if n_in_from == 0 and n_in_to == 0: + continue + elif n_in_from == 0: + # don't zero if all are ghosts + if n_in_to < 4: + zero_in_pert.append(torsion) + elif n_in_to == 0: + # don't zero if all are ghosts + if n_in_from < 4: + zero_in_ref.append(torsion) + else: + zero_in_pert.append(torsion) + zero_in_ref.append(torsion) + + if len(zero_in_ref) == 0 and len(zero_in_pert) == 0: + # nothing to do + return self + + mol = self._mol.molecule().edit() + + if len(zero_in_ref) > 0: + dihs = self._mol.property(self._map["dihedral0"]) + imps = self._mol.property(self._map["improper0"]) + + for torsion in zero_in_ref: + dihs.clear(torsion.id()) + imps.clear(torsion.id()) + + mol.set_property(self._map["dihedral0"].source(), dihs) + mol.set_property(self._map["improper0"].source(), imps) + + if len(zero_in_pert) > 0: + dihs = self._mol.property(self._map["dihedral1"]) + imps = self._mol.property(self._map["improper1"]) + + for torsion in zero_in_pert: + dihs.clear(torsion.id()) + imps.clear(torsion.id()) + + mol.set_property(self._map["dihedral1"].source(), dihs) + mol.set_property(self._map["improper1"].source(), imps) + + self._mol.update(mol.commit()) + + if auto_commit: + return self.commit() + else: + return self + + def to_openmm( + self, + constraint: str = None, + perturbable_constraint: str = None, + swap_end_states: bool = None, + include_constrained_energies: bool = None, + map: dict = None, + ): + """ + Return the PerturbableOpenMMMolecule that corresponds to + this perturbation in the OpenMM simulation. The arguments + to this function have the same meaning as the equivalents + in the dynamics() and minimisation() functions of a molecule. + + Parameters + ---------- + + constraint: str + The constraint algorithm to use + + perturbable_constraint: str + The constraint algorithm to use for perturbable atoms + + swap_end_states: bool + If True then the end states will be swapped + + include_constrained_energies: bool + If True then the constrained energies will be included + + map: dict + The property map to use + + + Returns + ------- + PerturbableOpenMMMolecule + The perturbable OpenMM molecule + """ + from ..base import create_map + + map = create_map(self._map, map) + + if constraint is not None: + map.set("constraint", str(constraint)) + + if perturbable_constraint is not None: + map.set("perturbable_constraint", str(perturbable_constraint)) + + if swap_end_states is not None: + map.set("swap_end_states", bool(swap_end_states)) + + if include_constrained_energies is not None: + map.set("include_constrained_energies", bool(include_constrained_energies)) + + from ..convert.openmm import PerturbableOpenMMMolecule + + try: + return PerturbableOpenMMMolecule(self._mol.molecule(), map=map) + except KeyError: + # probably need to choose an end-state - default to reference + return PerturbableOpenMMMolecule( + self._mol.perturbation().link_to_reference(auto_commit=True).molecule(), + map=map, + ) diff --git a/src/sire/options/_dynamics_options.py b/src/sire/options/_dynamics_options.py index ef63efcf2..4da2dcb25 100644 --- a/src/sire/options/_dynamics_options.py +++ b/src/sire/options/_dynamics_options.py @@ -46,15 +46,55 @@ class Constraint(_Option): NONE = "none", "Do not use constraints" AUTO = "auto", "Choose the constraints automatically" HBONDS = "h_bonds", "Constrain bonds involving hydrogens" + HBONDS_NOT_PERTURBED = ( + "h_bonds_not_perturbed", + "Constrain bonds involving hydrogens, excluding those that are perturbed", + ) + HBONDS_NOT_HEAVY_PERTURBED = ( + "h_bonds_not_heavy_perturbed", + "Constrain bonds involving hydrogens, excluding those that are perturbed " + "but do not involve a hydrogen in any end state.", + ) BONDS = "bonds", "Constrain all bonds" + BONDS_NOT_PERTURBED = ( + "bonds_not_perturbed", + "Constrain all bonds, excluding those are perturbed", + ) + BONDS_NOT_HEAVY_PERTURBED = ( + "bonds_not_heavy_perturbed", + "Constrain all bonds, excluding those that are perturbed but do not " + "involve a hydrogen in any end state.", + ) HBONDS_HANGLES = ( "h_bonds_h_angles", "Constrain bonds and angles involving hydrogens", ) + HBONDS_HANGLES_NOT_PERTURBED = ( + "h_bonds_h_angles_not_perturbed", + "Constrain bonds and angles involving hydrogens, " + "excluding those that are perturbed.", + ) + HBONDS_HANGLES_NOT_HEAVY_PERTURBED = ( + "h_bonds_h_angles_not_heavy_perturbed", + "Constrain bonds and angles involving hydrogens, " + "excluding those that are perturbed " + "but do not involve a hydrogen in any end state.", + ) BOND_HANGLES = ( "bonds_h_angles", "Constrain all bonds, and angles involving hydrogens", ) + BOND_HANGLES_NOT_PERTURBED = ( + "bonds_h_angles_not_perturbed", + "Constrain all bonds, and angles involving hydrogens, " + "excluding those that are perturbed", + ) + BONDS_HANGLES_NOT_HEAVY_PERTURBED = ( + "bonds_h_angles_not_heavy_perturbed", + "Constrain all bonds, and angles involving hydrogens, " + "excluding those that are perturbed " + "but do not involve a hydrogen in any end state.", + ) @staticmethod def create(option: str): @@ -88,9 +128,7 @@ def canonicalise(option: str): if option == "reaction_field" or option == "reaction field": return "rf" - elif ( - option == "particle_mesh_ewald" or option == "particle mesh ewald" - ): + elif option == "particle_mesh_ewald" or option == "particle mesh ewald": return "pme" elif option == "no_cutoff" or option == "no cutoff": return "none" diff --git a/src/sire/system/_system.py b/src/sire/system/_system.py index 50d866831..a2eb05a77 100644 --- a/src/sire/system/_system.py +++ b/src/sire/system/_system.py @@ -417,6 +417,11 @@ def minimisation(self, *args, **kwargs): is useful if you just want to run standard molecular dynamics of the reference or perturbed states. + shift_coulomb: length + The shift_coulomb parameter that controls the electrostatic + softening potential that smooths the creation and deletion + of ghost atoms during a potential. This defaults to 1.0 A. + shift_delta: length The shift_delta parameter that controls the electrostatic and van der Waals softening potential that smooths the @@ -453,6 +458,14 @@ def minimisation(self, *args, **kwargs): minimisation. This would be CUDA_DEVICE_ID or similar if CUDA was used. This can be any valid OpenMM device string + dynamic_constraints: bool + Whether or not to update the length of constraints of + perturbable bonds with lambda. This defaults to True, + meaning that changing lambda will change any constraint + on a perturbable bond to equal to the value of r0 at + that lambda value. If this is false, then the constraint + is set based on the current length. + map: dict A dictionary of additional options. Note that any options set in this dictionary that are also specified via one of @@ -573,6 +586,11 @@ def dynamics(self, *args, **kwargs): replaced by a `sire.vol.Cartesian` space, and the simulation run in vacuum. + shift_coulomb: length + The shift_coulomb parameter that controls the electrostatic + softening potential that smooths the creation and deletion + of ghost atoms during a potential. This defaults to 1.0 A. + shift_delta: length The shift_delta parameter that controls the electrostatic and van der Waals softening potential that smooths the @@ -607,6 +625,24 @@ def dynamics(self, *args, **kwargs): The desired precision for the simulation (e.g. `single`, `mixed` or `double`) + com_reset_frequency: + Either the number of steps between center-of-mass resets, + or the time between resets. If this is unset, then + the center-of-mass is not reset during the simulation. + + barostat_frequency: + Either the number of steps between MC moves to apply the + barostat, of the time between moves. If this is unset, + then the default of every 25 steps is used. + + dynamic_constraints: bool + Whether or not to update the length of constraints of + perturbable bonds with lambda. This defaults to True, + meaning that changing lambda will change any constraint + on a perturbable bond to equal to the value of r0 at + that lambda value. If this is false, then the constraint + is set based on the current length. + map: dict A dictionary of additional options. Note that any options set in this dictionary that are also specified via one of @@ -778,7 +814,11 @@ def set_time(self, time, map=None): self._molecules = None def energy_trajectory( - self, to_pandas: bool = False, to_alchemlyb: bool = False, map=None + self, + to_pandas: bool = False, + to_alchemlyb: bool = False, + energy_unit: str = "kcal/mol", + map=None, ): """ Return the energy trajectory for this System. This is the history @@ -795,6 +835,12 @@ def energy_trajectory( to_alchemlyb: bool Whether or not to return the energy trajectory as a pandas DataFrame that is formatted to usable in alchemlyb + + energy_unit: str + Whichever of the alchemlyb energy units you want the output + DataFrame to use. This is in alchemlyb format, e.g. + `kcal/mol`, `kJ/mol`, or `kT`. This is only used if + `to_alchemlyb` is True. """ from ..base import create_map @@ -827,7 +873,9 @@ def energy_trajectory( if to_pandas or to_alchemlyb: try: - return traj.to_pandas(to_alchemlyb=to_alchemlyb) + return traj.to_pandas( + to_alchemlyb=to_alchemlyb, energy_unit=energy_unit + ) except Exception: ensemble = self.ensemble() @@ -837,7 +885,9 @@ def energy_trajectory( temperature = None return traj.to_pandas( - to_alchemlyb=to_alchemlyb, temperature=temperature + to_alchemlyb=to_alchemlyb, + temperature=temperature, + energy_unit=energy_unit, ) else: return traj diff --git a/tests/cas/test_lambdaschedule.py b/tests/cas/test_lambdaschedule.py new file mode 100644 index 000000000..ff4487908 --- /dev/null +++ b/tests/cas/test_lambdaschedule.py @@ -0,0 +1,89 @@ +import sire as sr +import pytest +import random + + +def _assert_same_equation(x, eq1, eq2): + for i in range(100): + val = {x: random.uniform(0, 1)} + + assert eq1.evaluate(val) == pytest.approx(eq2.evaluate(val), 1e-5) + + +def test_lambdaschedule(): + l = sr.cas.LambdaSchedule.standard_morph() + + morph_equation = (1 - l.lam()) * l.initial() + l.lam() * l.final() + morph2_equation = (1 - l.lam() ** 2) * l.initial() + l.lam() ** 2 * l.final() + morph3_equation = l.lam() * l.initial() + morph4_equation = (1 - l.lam()) * l.final() + morph5_equation = l.lam() + + assert l.get_stages() == ["morph"] + + assert len(l.get_levers()) == 0 + assert len(l.get_forces()) == 0 + + _assert_same_equation(l.lam(), l.get_equation("morph"), morph_equation) + + l.set_equation(stage="morph", lever="charge", equation=morph2_equation) + + assert l.get_stages() == ["morph"] + assert l.get_levers() == ["charge"] + assert len(l.get_forces()) == 0 + + _assert_same_equation(l.lam(), l.get_equation("morph", "charge"), morph2_equation) + + l.set_equation(stage="morph", force="CLJ", equation=morph3_equation) + + assert l.get_stages() == ["morph"] + assert l.get_levers() == ["charge"] + assert l.get_forces() == ["CLJ"] + + _assert_same_equation( + l.lam(), l.get_equation(stage="morph", force="charge"), morph3_equation + ) + + l.set_equation(stage="morph", force="CLJ", lever="LJ", equation=morph4_equation) + + assert l.get_stages() == ["morph"] + assert l.get_levers() == ["charge", "LJ"] + assert l.get_forces() == ["CLJ"] + + _assert_same_equation(l.lam(), l.get_equation(stage="morph"), morph_equation) + + _assert_same_equation( + l.lam(), l.get_equation(stage="morph", force="charge"), morph2_equation + ) + + _assert_same_equation( + l.lam(), l.get_equation(stage="morph", force="charge"), morph3_equation + ) + + _assert_same_equation( + l.lam(), l.get_equation(stage="morph", force="CLJ", lever="LJ"), morph4_equation + ) + + l.prepend_stage("scale_up", morph5_equation) + + assert l.get_stages() == ["scale_up", "morph"] + + assert l.get_stages() == ["scale_up", "morph"] + assert l.get_levers() == ["charge", "LJ"] + assert l.get_forces() == ["CLJ"] + + _assert_same_equation(l.lam(), l.get_equation(stage="morph"), morph_equation) + + _assert_same_equation( + l.lam(), l.get_equation(stage="morph", force="charge"), morph2_equation + ) + + _assert_same_equation( + l.lam(), l.get_equation(stage="morph", force="charge"), morph3_equation + ) + + _assert_same_equation( + l.lam(), l.get_equation(stage="morph", force="CLJ", lever="LJ"), morph4_equation + ) + + _assert_same_equation(l.lam(), l.get_equation(stage="scale_up"), morph5_equation) diff --git a/tests/conftest.py b/tests/conftest.py index e54227950..694193e19 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -187,6 +187,11 @@ def neopentane_methane(): return sr.load_test_files("neo_meth_scratch.bss") +@pytest.fixture(scope="session") +def solvated_neopentane_methane(): + return sr.load_test_files("neo_meth_solv.bss") + + @pytest.fixture(scope="session") def zero_lj_mols(): return sr.load_test_files("zero_lj.prm7", "zero_lj.rst7") diff --git a/tests/convert/test_openmm.py b/tests/convert/test_openmm.py index fa0056fc9..90d4dff1b 100644 --- a/tests/convert/test_openmm.py +++ b/tests/convert/test_openmm.py @@ -10,20 +10,8 @@ def test_openmm_single_energy_neopentane(neopentane_methane, openmm_platform): mol = neopentane_methane[0] # this function will extract the lambda0 or lambda1 end state - def get_end_state(mol, state, remove_state): - c = mol.cursor() - for key in c.keys(): - if key.endswith(state): - c[key.removesuffix(state)] = c[key] - del c[key] - elif key.endswith(remove_state): - del c[key] - - c["is_perturbable"] = False - return c.commit() - - mol0 = get_end_state(mol, "0", "1") - mol1 = get_end_state(mol, "1", "0") + mol0 = sr.morph.extract_reference(mol) + mol1 = sr.morph.extract_perturbed(mol) map = { "space": sr.vol.Cartesian(), @@ -41,7 +29,7 @@ def get_end_state(mol, state, remove_state): energy0 = energy0.value_in_unit(energy0.unit) assert mol0.energy(map=map).to(sr.units.kJ_per_mol) == pytest.approx( - energy0, abs=0.1 + energy0, abs=1e-3 ) omm1 = sr.convert.to(mol1, "openmm", map=map) @@ -53,9 +41,17 @@ def get_end_state(mol, state, remove_state): energy1 = energy1.value_in_unit(energy1.unit) assert mol1.energy(map=map).to(sr.units.kJ_per_mol) == pytest.approx( - energy1, abs=0.1 + energy1, abs=1e-3 ) + assert mol0.dynamics(map=map, cutoff="none").current_potential_energy().to( + sr.units.kJ_per_mol + ) == pytest.approx(energy0, abs=1e-3) + + assert mol1.dynamics(map=map, cutoff="none").current_potential_energy().to( + sr.units.kJ_per_mol + ) == pytest.approx(energy1, abs=1e-3) + @pytest.mark.skipif( "openmm" not in sr.convert.supported_formats(), diff --git a/tests/convert/test_openmm_constraints.py b/tests/convert/test_openmm_constraints.py new file mode 100644 index 000000000..8eecc15ed --- /dev/null +++ b/tests/convert/test_openmm_constraints.py @@ -0,0 +1,214 @@ +import sire as sr +import pytest + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_h_bond_constraints(merged_ethane_methanol, openmm_platform): + mols = sr.morph.link_to_reference(merged_ethane_methanol) + + d = mols[0].dynamics(constraint="none", platform=openmm_platform) + + constraints = d.get_constraints() + + # no constraints + assert len(constraints) == 0 + + d = mols[0].dynamics( + constraint="h-bonds", dynamic_constraints=False, platform=openmm_platform + ) + + constraints = d.get_constraints() + + # there are 6 bonds involving hydrogen + assert len(constraints) == 6 + + for constraint in constraints: + # check for a single H atom + assert constraint[0].atom("element H").element().symbol() == "H" + + d = mols[0].dynamics( + constraint="bonds", dynamic_constraints=False, platform=openmm_platform + ) + + constraints = d.get_constraints() + + assert len(constraints) == len(mols[0].bonds()) + + d = mols[0].dynamics( + constraint="bonds", perturbable_constraint="none", platform=openmm_platform + ) + + # should be no constraints + assert len(d.get_constraints()) == 0 + + d = mols[0].dynamics( + constraint="bonds", + perturbable_constraint="h-bonds", + ignore_perturbations=True, + platform=openmm_platform, + ) + + # should be full bonds constraints + assert len(d.get_constraints()) == len(mols[0].bonds()) + + d = mols[0].dynamics(constraint="h-bonds-not-perturbed", platform=openmm_platform) + + constraints = d.get_constraints() + + # there should only be 2 bonds that are not constrained (C-C/O and C/O-H/G) + assert len(constraints) == len(mols[0].bonds()) - 2 + + for constraint in constraints: + # check for a single H atom + assert constraint[0].atom("element H").element().symbol() == "H" + + d = mols[0].dynamics(constraint="bonds-not-perturbed", platform=openmm_platform) + + # there should only be 2 bonds that are constrained (C-C/O and C/O-H/G) + constraints = d.get_constraints() + + assert len(d.get_constraints()) == len(mols[0].bonds()) - 2 + + d = mols[0].dynamics( + constraint="none", + perturbable_constraint="h-bonds-not-perturbed", + platform=openmm_platform, + ) + + assert len(d.get_constraints()) == len(mols[0].bonds()) - 2 + + d = mols[0].dynamics( + constraint="none", + perturbable_constraint="bonds-not-perturbed", + platform=openmm_platform, + ) + + assert len(d.get_constraints()) == len(mols[0].bonds()) - 2 + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_neo_constraints(neopentane_methane, openmm_platform): + mols_fwds = sr.morph.link_to_reference(neopentane_methane) + mols_bwds = sr.morph.link_to_perturbed(neopentane_methane) + + mols_fwds = sr.morph.repartition_hydrogen_masses(mols_fwds) + mols_bwds = sr.morph.repartition_hydrogen_masses(mols_bwds) + + d_fwds = mols_fwds.dynamics(constraint="none", platform=openmm_platform) + d_bwds = mols_bwds.dynamics( + constraint="none", swap_end_states=True, platform=openmm_platform + ) + + c_fwds = d_fwds.get_constraints() + c_bwds = d_bwds.get_constraints() + + assert len(c_fwds) == len(c_bwds) == 0 + + d_fwds = mols_fwds.dynamics(constraint="bonds", platform=openmm_platform) + d_bwds = mols_bwds.dynamics( + constraint="bonds", swap_end_states=True, platform=openmm_platform + ) + + c_fwds = d_fwds.get_constraints() + c_bwds = d_bwds.get_constraints() + + assert len(c_fwds) == len(c_bwds) == len(mols_fwds[0].bonds()) + + for f, b in zip(c_fwds, c_bwds): + assert f[0][0].name() == b[0][0].name() + assert f[0][1].name() == b[0][1].name() + + d_fwds = mols_fwds.dynamics(constraint="h-bonds", platform=openmm_platform) + d_bwds = mols_bwds.dynamics( + constraint="h-bonds", swap_end_states=True, platform=openmm_platform + ) + + c_fwds = d_fwds.get_constraints() + c_bwds = d_bwds.get_constraints() + + # this ends up constraining all bonds, as in either end state + # they will always have a hydrogen + assert len(c_fwds) == len(c_bwds) == len(mols_fwds[0].bonds()) + + for f, b in zip(c_fwds, c_bwds): + assert f[0][0].name() == b[0][0].name() + assert f[0][1].name() == b[0][1].name() + + # there should be one dynamic constraint, of the C-C/H bond + p = mols_fwds[0].perturbation().to_openmm(constraint="h-bonds") + assert len(p.changed_constraints(to_pandas=False)) == 1 + + d_fwds = mols_fwds.dynamics( + constraint="bonds-not-perturbed", platform=openmm_platform + ) + + d_bwds = mols_bwds.dynamics( + constraint="bonds-not-perturbed", swap_end_states=True, platform=openmm_platform + ) + + c_fwds = d_fwds.get_constraints() + c_bwds = d_bwds.get_constraints() + + # there should be no dynamic constraints + p = mols_fwds[0].perturbation().to_openmm(constraint="bonds-not-perturbed") + assert len(p.changed_constraints(to_pandas=False)) == 0 + + # the perturbing C-C/H bond should not be constrained + assert len(c_fwds) == len(c_bwds) == len(mols_fwds[0].bonds()) - 1 + + for f, b in zip(c_fwds, c_bwds): + assert f[0][0].name() == b[0][0].name() + assert f[0][1].name() == b[0][1].name() + + d_fwds = mols_fwds.dynamics( + constraint="h-bonds-not-perturbed", + platform=openmm_platform, + ) + + d_bwds = mols_bwds.dynamics( + constraint="h-bonds-not-perturbed", + platform=openmm_platform, + swap_end_states=True, + ) + + c_fwds = d_fwds.get_constraints() + c_bwds = d_bwds.get_constraints() + + assert len(c_fwds) == len(c_bwds) + + for f, b in zip(c_fwds, c_bwds): + assert f[0][0].name() == b[0][0].name() + assert f[0][1].name() == b[0][1].name() + + d_fwds = mols_fwds.dynamics( + constraint="h-bonds-not-heavy-perturbed", + platform=openmm_platform, + ) + + d_bwds = mols_bwds.dynamics( + constraint="h-bonds-not-heavy-perturbed", + platform=openmm_platform, + swap_end_states=True, + ) + + c_fwds = d_fwds.get_constraints() + c_bwds = d_bwds.get_constraints() + + # this should have constrained everything, as everything + # is either a H bond or a perturbable bond containing H + # at either end state + assert len(c_fwds) == len(c_bwds) == len(mols_fwds[0].bonds()) + + for f, b in zip(c_fwds, c_bwds): + assert f[0][0].name() == b[0][0].name() + assert f[0][1].name() == b[0][1].name() + + # check that there is only one perturbable constraint + p = mols_fwds[0].perturbation().to_openmm(constraint="h-bonds-not-heavy-perturbed") + assert (len(p.changed_constraints(to_pandas=False))) == 1 diff --git a/tests/convert/test_openmm_dynamic_constraints.py b/tests/convert/test_openmm_dynamic_constraints.py new file mode 100644 index 000000000..25e4a6719 --- /dev/null +++ b/tests/convert/test_openmm_dynamic_constraints.py @@ -0,0 +1,86 @@ +import sire as sr +import pytest + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_dynamic_constraints(merged_ethane_methanol, openmm_platform): + mols = sr.morph.link_to_reference(merged_ethane_methanol) + + d = mols[0].dynamics(constraint="bonds", platform=openmm_platform) + + d.set_lambda(0.0) + + constraints_0 = d.get_constraints() + + def get_r0(bond, state="0"): + return sr.legacy.MM.AmberBond( + bond.potential(map={"bond": f"bond{state}"}), sr.legacy.CAS.r + ).r0() + + for constraint in constraints_0: + bond = sr.mm.Bond(constraint[0][0], constraint[0][1]) + r0_0 = get_r0(bond, "0") + assert constraint[1].value() == pytest.approx(r0_0) + + d.set_lambda(1.0) + + constraints_1 = d.get_constraints() + + assert len(constraints_0) == len(constraints_1) + + for constraint in constraints_1: + bond = sr.mm.Bond(constraint[0][0], constraint[0][1]) + r0_1 = get_r0(bond, "1") + assert constraint[1].value() == pytest.approx(r0_1) + + d.set_lambda(0.5) + + constraints_05 = d.get_constraints() + + assert len(constraints_0) == len(constraints_05) + + for constraint in constraints_05: + bond = sr.mm.Bond(constraint[0][0], constraint[0][1]) + r0_0 = get_r0(bond, "0") + r0_1 = get_r0(bond, "1") + assert constraint[1].value() == pytest.approx(0.5 * (r0_0 + r0_1)) + + d.set_lambda(0.0, update_constraints=False) + + assert constraints_05 == d.get_constraints() + + d = mols[0].dynamics( + constraint="bonds", dynamic_constraints=False, platform=openmm_platform + ) + + d.set_lambda(0.0) + + constraints_0 = d.get_constraints() + + for constraint in constraints_0: + bond = sr.mm.Bond(constraint[0][0], constraint[0][1]) + r0_0 = get_r0(bond, "0") + assert constraint[1].value() == pytest.approx(r0_0) + + d.set_lambda(1.0) + + constraints_1 = d.get_constraints() + + for constraint in constraints_1: + bond = sr.mm.Bond(constraint[0][0], constraint[0][1]) + r0_0 = get_r0(bond, "0") + assert constraint[1].value() == pytest.approx(r0_0) + assert constraint in constraints_0 + + d.set_lambda(0.5) + + constraints_05 = d.get_constraints() + + for constraint in constraints_05: + bond = sr.mm.Bond(constraint[0][0], constraint[0][1]) + r0_0 = get_r0(bond, "0") + assert constraint[1].value() == pytest.approx(r0_0) + assert constraint in constraints_0 diff --git a/tests/convert/test_openmm_lambda.py b/tests/convert/test_openmm_lambda.py index e992452b6..ace7e09e3 100644 --- a/tests/convert/test_openmm_lambda.py +++ b/tests/convert/test_openmm_lambda.py @@ -3,6 +3,11 @@ def _run_test(mols, is_slow=False, use_taylor=False, precision=1e-3, platform="CPU"): + try: + space = mols.space() + except Exception: + space = sr.vol.Cartesian() + c = mols.cursor() # can only get the same energies if they have the same coordinates @@ -58,7 +63,10 @@ def get_end_state(mol, state, remove_state): map = { "platform": platform, "schedule": l, - "constraint": "bonds-h-angles", + "constraint": "h-bonds-not-perturbed", + "include_constrained_energies": True, + "dynamic_constraints": False, + "space": space, } if use_taylor: @@ -74,21 +82,27 @@ def get_end_state(mol, state, remove_state): omm1 = sr.convert.to(mols1, "openmm", map=map) nrg1 = omm1.get_energy().value() + omm.set_lambda(1.0) + assert omm.get_energy().value() == pytest.approx(nrg1, precision) + omm.set_lambda(0.0) assert omm.get_energy().value() == pytest.approx(nrg0, precision) omm.set_lambda(0.5) nrg0_5 = omm.get_energy().value() - omm.set_lambda(1.0) - assert omm.get_energy().value() == pytest.approx(nrg1, precision) - omm.set_lambda(0.5) assert omm.get_energy().value() == pytest.approx(nrg0_5, precision) omm.set_lambda(0.0) assert omm.get_energy().value() == pytest.approx(nrg0, precision) + omm.set_lambda(0.5) + assert omm.get_energy().value() == pytest.approx(nrg0_5, precision) + + omm.set_lambda(1.0) + assert omm.get_energy().value() == pytest.approx(nrg1, precision) + # now swap the end states - lambda 0 == 1 and lambda 1 == 0 map["swap_end_states"] = True @@ -110,6 +124,12 @@ def get_end_state(mol, state, remove_state): omm.set_lambda(0.0) assert omm.get_energy().value() == pytest.approx(nrg1, precision) + omm.set_lambda(0.5) + assert omm.get_energy().value() == pytest.approx(nrg0_5, precision) + + omm.set_lambda(1.0) + assert omm.get_energy().value() == pytest.approx(nrg0, precision) + @pytest.mark.skipif( "openmm" not in sr.convert.supported_formats(), @@ -173,3 +193,532 @@ def test_openmm_scale_lambda_cyclopentane(pentane_cyclopentane, openmm_platform) mols.update(mol) _run_test(mols, False, platform=openmm_platform) + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_openmm_scale_lambda_neopentane_methane(neopentane_methane, openmm_platform): + _run_test(neopentane_methane, False, platform=openmm_platform) + + +@pytest.mark.veryslow +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_big_openmm_scale_lambda_neopentane_methane_solv( + solvated_neopentane_methane, openmm_platform +): + _run_test(solvated_neopentane_methane, True, platform=openmm_platform) + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_openmm_scale_lambda_neopentane_methane_solv( + solvated_neopentane_methane, openmm_platform +): + _run_test(solvated_neopentane_methane, False, platform=openmm_platform) + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_solvated_neopentane_methane_scan(solvated_neopentane_methane, openmm_platform): + mols = sr.morph.link_to_reference(solvated_neopentane_methane) + + mols = sr.morph.repartition_hydrogen_masses(mols) + + mols = sr.morph.zero_ghost_torsions(mols) + + # these were calculated using somd, no constraints, no cutoff, no space + expected = { + 0.0: -6845.34, + 1.0: -6759.75, + } + + d = mols.dynamics( + constraint="none", + cutoff="none", + cutoff_type="RF", + platform=openmm_platform, + map={"space": sr.vol.Cartesian()}, + ) + + for lam_val, nrg in expected.items(): + d.set_lambda(lam_val) + calc_nrg = d.current_potential_energy().value() + assert calc_nrg == pytest.approx(nrg, abs=1e-2) + + # these were calculated using somd, no constraints, 10 A cutoff + expected = { + 0.0: -8330.43, + 1.0: -8242.37, + } + + d = mols.dynamics( + constraint="none", + cutoff="10 A", + cutoff_type="RF", + platform=openmm_platform, + ) + + for lam_val, nrg in expected.items(): + d.set_lambda(lam_val) + calc_nrg = d.current_potential_energy().value() + assert calc_nrg == pytest.approx(nrg, abs=1e-2) + + # these were calculated using somd, h-bonds-not-perturbed, 10 A cutoff + expected = { + 0.0: -8331.14, + 1.0: -8243.08, + } + + d = mols.dynamics( + constraint="bonds_not_perturbed", + cutoff="10 A", + cutoff_type="RF", + include_constrained_energies=False, + platform=openmm_platform, + ) + + for lam_val, nrg in expected.items(): + d.set_lambda(lam_val) + calc_nrg = d.current_potential_energy().value() + assert calc_nrg == pytest.approx(nrg, abs=1e-2) + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_neopentane_methane_scan(neopentane_methane, openmm_platform): + mols = sr.morph.link_to_reference(neopentane_methane) + + mols = sr.morph.repartition_hydrogen_masses(mols) + + # these were calculated using somd, no constraints + expected_none = { + 0.0: -2.85704, + 0.1: -3.98964, + 0.2: -1.29653, + 0.3: 2.8749, + 0.4: 8.20206, + 0.5: 14.704, + 0.6: 22.4421, + 0.7: 31.4726, + 0.8: 41.8432, + 0.9: 53.5963, + 1.0: 66.773, + } + + # these were calculated using somd, hbonds + expected_hbonds = { + 0.0: -3.70711, + 0.1: -5.5007, + 0.2: -4.67283, + 0.3: -3.60548, + 0.4: -2.65583, + 0.5: -1.83948, + 0.6: -1.12953, + 0.7: -0.504399, + 0.8: 0.0490519, + 0.9: 0.538499, + 1.0: 0.970649, + } + + # these were calculated using somd, hbonds_notperturbed + expected_hbonds_not_perturbed = { + 0.0: -3.70499, + 0.1: -4.83759, + 0.2: -2.14448, + 0.3: 2.02695, + 0.4: 7.35411, + 0.5: 13.856, + 0.6: 21.5941, + 0.7: 30.6246, + 0.8: 40.9953, + 0.9: 52.7483, + 1.0: 65.9251, + } + + d = mols.dynamics(constraint="none", cutoff="10 A", platform=openmm_platform) + + calc_none = {} + + for lam_val, nrg in expected_none.items(): + d.set_lambda(lam_val) + calc_none[lam_val] = d.current_potential_energy().value() + + d = mols.dynamics( + constraint="bonds_not_perturbed", + cutoff="10 A", + include_constrained_energies=True, + platform=openmm_platform, + ) + + calc_hbonds_not_perturbed = {} + + for lam_val, nrg in expected_hbonds_not_perturbed.items(): + d.set_lambda(lam_val) + calc_hbonds_not_perturbed[lam_val] = d.current_potential_energy().value() + + d = mols.dynamics( + constraint="bonds_not_perturbed", + cutoff="10 A", + include_constrained_energies=False, + platform=openmm_platform, + ) + + calc_hbonds_not_perturbed_no_energy = {} + + for lam_val, nrg in expected_hbonds_not_perturbed.items(): + d.set_lambda(lam_val) + calc_hbonds_not_perturbed_no_energy[ + lam_val + ] = d.current_potential_energy().value() + + d = mols.dynamics( + constraint="bonds-not-perturbed", + cutoff="10 A", + include_constrained_energies=False, + swap_end_states=True, + platform=openmm_platform, + ) + + calc_hbonds_not_perturbed_no_energy_swap = {} + + for lam_val, nrg in expected_hbonds_not_perturbed.items(): + d.set_lambda(lam_val) + calc_hbonds_not_perturbed_no_energy_swap[ + lam_val + ] = d.current_potential_energy().value() + + d = mols.dynamics( + constraint="h-bonds", + cutoff="10 A", + include_constrained_energies=False, + platform=openmm_platform, + ) + + calc_hbonds_no_energy = {} + + for lam_val, nrg in expected_hbonds.items(): + d.set_lambda(lam_val) + calc_hbonds_no_energy[lam_val] = d.current_potential_energy().value() + + # should match the no_constraints somd energy at the end points + assert calc_none[0.0] == pytest.approx(expected_none[0.0], 1e-3) + assert calc_none[1.0] == pytest.approx(expected_none[1.0], 1e-3) + + assert calc_hbonds_not_perturbed[0.0] == pytest.approx(expected_none[0.0], 1e-3) + assert calc_hbonds_not_perturbed[1.0] == pytest.approx(expected_none[1.0], 1e-3) + + assert calc_hbonds_no_energy[0.0] == pytest.approx(expected_hbonds[0.0], 1e-2) + assert calc_hbonds_no_energy[1.0] == pytest.approx(expected_hbonds[1.0], 1e-2) + + # but the hbonds_not_perturbed energy should be different if constraints + # are not included - should equal to the somd constraints energy + # (somd does not calculate energies of constrained bonds) + assert calc_hbonds_not_perturbed_no_energy[0.0] == pytest.approx( + expected_hbonds_not_perturbed[0.0], 1e-2 + ) + assert calc_hbonds_not_perturbed_no_energy[1.0] == pytest.approx( + expected_hbonds_not_perturbed[1.0], 1e-2 + ) + + assert calc_hbonds_not_perturbed_no_energy_swap[0.0] == pytest.approx( + expected_hbonds_not_perturbed[1.0], 1e-2 + ) + + assert calc_hbonds_not_perturbed_no_energy_swap[1.0] == pytest.approx( + expected_hbonds_not_perturbed[0.0], 1e-2 + ) + + for lam_val in expected_none.keys(): + # Including the energy should give the same energy regardless + # of whether constraints are included or not + assert calc_none[lam_val] == pytest.approx( + calc_hbonds_not_perturbed[lam_val], 1e-3 + ) + + # But not including the constraints should give a different energy + assert calc_none[lam_val] != calc_hbonds_not_perturbed_no_energy[lam_val] + + # The paths through lambda space for somd and sire will be slightly + # different - but should be consistently different comparing + # including and not including constraints + for lam_val in expected_none.keys(): + assert calc_none[lam_val] - calc_hbonds_not_perturbed_no_energy[ + lam_val + ] == pytest.approx( + expected_none[lam_val] - expected_hbonds_not_perturbed[lam_val], 1e-2 + ) + + # check backwards is the reverse of forwards + lamvals_f = list(calc_hbonds_not_perturbed_no_energy.keys()) + lamvals_b = list(calc_hbonds_not_perturbed_no_energy_swap.keys()) + + lamvals_b.reverse() + + for lam_f, lam_b in zip(lamvals_f, lamvals_b): + assert calc_hbonds_not_perturbed_no_energy[lam_f] == pytest.approx( + calc_hbonds_not_perturbed_no_energy_swap[lam_b], 1e-3 + ) + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_neopentane_methane_scan_no_cutoff(neopentane_methane, openmm_platform): + mols = sr.morph.link_to_reference(neopentane_methane) + + mols = sr.morph.repartition_hydrogen_masses(mols) + + # these were calculated using somd, no constraints + expected_none = { + 0.0: 0.0158906, + 0.1: -1.55981, + 0.2: 0.72506, + 0.3: 4.523, + 0.4: 9.51135, + 0.5: 15.709, + 0.6: 23.1774, + 0.7: 31.9727, + 0.8: 42.1424, + 0.9: 53.7287, + 1.0: 66.773, + } + + # these were calculated using somd, hbonds_notperturbed + expected_hbonds_not_perturbed = { + 0.0: -0.832059, + 0.1: -2.40776, + 0.2: -0.12289, + 0.3: 3.67505, + 0.4: 8.6634, + 0.5: 14.8611, + 0.6: 22.3295, + 0.7: 31.1247, + 0.8: 41.2944, + 0.9: 52.8808, + 1.0: 65.9251, + } + + d = mols.dynamics(constraint="none", cutoff="infinite", platform=openmm_platform) + + calc_none = {} + + for lam_val, nrg in expected_none.items(): + d.set_lambda(lam_val) + calc_none[lam_val] = d.current_potential_energy().value() + + d = mols.dynamics( + constraint="bonds_not_perturbed", + cutoff="infinite", + include_constrained_energies=True, + platform=openmm_platform, + ) + + calc_hbonds_not_perturbed = {} + + for lam_val, nrg in expected_hbonds_not_perturbed.items(): + d.set_lambda(lam_val) + calc_hbonds_not_perturbed[lam_val] = d.current_potential_energy().value() + + d = mols.dynamics( + constraint="bonds_not_perturbed", + cutoff="infinite", + include_constrained_energies=False, + platform=openmm_platform, + ) + + calc_hbonds_not_perturbed_no_energy = {} + + for lam_val, nrg in expected_hbonds_not_perturbed.items(): + d.set_lambda(lam_val) + calc_hbonds_not_perturbed_no_energy[ + lam_val + ] = d.current_potential_energy().value() + + d = mols.dynamics( + constraint="bonds-not-perturbed", + cutoff="infinite", + include_constrained_energies=False, + swap_end_states=True, + platform=openmm_platform, + ) + + calc_hbonds_not_perturbed_no_energy_swap = {} + + for lam_val, nrg in expected_hbonds_not_perturbed.items(): + d.set_lambda(lam_val) + calc_hbonds_not_perturbed_no_energy_swap[ + lam_val + ] = d.current_potential_energy().value() + + # should match the no_constraints somd energy at the end points + assert calc_none[0.0] == pytest.approx(expected_none[0.0], abs=1e-2) + assert calc_none[1.0] == pytest.approx(expected_none[1.0], 1e-2) + + assert calc_hbonds_not_perturbed[0.0] == pytest.approx(expected_none[0.0], abs=1e-3) + assert calc_hbonds_not_perturbed[1.0] == pytest.approx(expected_none[1.0], 1e-3) + + # but the hbonds_not_perturbed energy should be different if constraints + # are not included - should equal to the somd constraints energy + # (somd does not calculate energies of constrained bonds) + assert calc_hbonds_not_perturbed_no_energy[0.0] == pytest.approx( + expected_hbonds_not_perturbed[0.0], 1e-2 + ) + assert calc_hbonds_not_perturbed_no_energy[1.0] == pytest.approx( + expected_hbonds_not_perturbed[1.0], 1e-2 + ) + + assert calc_hbonds_not_perturbed_no_energy_swap[0.0] == pytest.approx( + expected_hbonds_not_perturbed[1.0], 1e-2 + ) + + assert calc_hbonds_not_perturbed_no_energy_swap[1.0] == pytest.approx( + expected_hbonds_not_perturbed[0.0], 1e-2 + ) + + for lam_val in expected_none.keys(): + # Including the energy should give the same energy regardless + # of whether constraints are included or not + assert calc_none[lam_val] == pytest.approx( + calc_hbonds_not_perturbed[lam_val], 1e-3 + ) + + # But not including the constraints should give a different energy + assert calc_none[lam_val] != calc_hbonds_not_perturbed_no_energy[lam_val] + + # The paths through lambda space for somd and sire will be slightly + # different - but should be consistently different comparing + # including and not including constraints + for lam_val in expected_none.keys(): + assert calc_none[lam_val] - calc_hbonds_not_perturbed_no_energy[ + lam_val + ] == pytest.approx( + expected_none[lam_val] - expected_hbonds_not_perturbed[lam_val], 1e-2 + ) + + # check backwards is the reverse of forwards + lamvals_f = list(calc_hbonds_not_perturbed_no_energy.keys()) + lamvals_b = list(calc_hbonds_not_perturbed_no_energy_swap.keys()) + + lamvals_b.reverse() + + for lam_f, lam_b in zip(lamvals_f, lamvals_b): + assert calc_hbonds_not_perturbed_no_energy[lam_f] == pytest.approx( + calc_hbonds_not_perturbed_no_energy_swap[lam_b], 1e-3 + ) + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_neopentane_methane_no_charge(neopentane_methane, openmm_platform): + mols = sr.morph.link_to_reference(neopentane_methane) + + mols = sr.morph.repartition_hydrogen_masses(mols) + + # these were calculated using somd, hbonds + expected_hbonds = { + 0.0: 2.69409, + 0.1: -0.00735767, + 0.2: -0.0258584, + 0.3: 0.2558, + 0.4: 0.479681, + 0.5: 0.629462, + 0.6: 0.731321, + 0.7: 0.806166, + 0.8: 0.866469, + 0.9: 0.919267, + 1.0: 0.970649, + } + + # these were calculated using somd, hbonds-notperturbed + expected_hbonds_not_perturbed = { + 0.0: 2.69621, + 0.1: 0.655753, + 0.2: 2.50249, + 0.3: 5.88823, + 0.4: 10.4896, + 0.5: 16.3249, + 0.6: 23.455, + 0.7: 31.9352, + 0.8: 41.8127, + 0.9: 53.1291, + 1.0: 65.9251, + } + + # these were calculated using somd, no constraints + expected_none = { + 0.0: 3.54416, + 0.1: 1.5037, + 0.2: 3.35044, + 0.3: 6.73618, + 0.4: 11.3376, + 0.5: 17.1729, + 0.6: 24.3029, + 0.7: 32.7831, + 0.8: 42.6606, + 0.9: 53.977, + 1.0: 66.773, + } + + d = mols.dynamics( + constraint="none", + cutoff="10 A", + platform=openmm_platform, + ) + + # Use the schedule to set all charges to zero + s = d.get_schedule() + s.set_equation(stage="morph", lever="charge", equation=0.0) + d.set_schedule(s) + + for lam_val, nrg in expected_none.items(): + d.set_lambda(lam_val) + calc = d.current_potential_energy().value() + assert calc == pytest.approx(nrg, abs=1e-4) + + d = mols.dynamics( + constraint="h-bonds", + cutoff="10 A", + include_constrained_energies=False, + platform=openmm_platform, + ) + + # Use the schedule to set all charges to zero + s = d.get_schedule() + s.set_equation(stage="morph", lever="charge", equation=0.0) + d.set_schedule(s) + + for lam_val, nrg in expected_hbonds.items(): + d.set_lambda(lam_val) + calc = d.current_potential_energy().value() + assert calc == pytest.approx(nrg, abs=1e-4) + + d = mols.dynamics( + constraint="h-bonds-not-perturbed", + cutoff="10 A", + include_constrained_energies=False, + platform=openmm_platform, + ) + + # Use the schedule to set all charges to zero + s = d.get_schedule() + s.set_equation(stage="morph", lever="charge", equation=0.0) + d.set_schedule(s) + + for lam_val, nrg in expected_hbonds_not_perturbed.items(): + d.set_lambda(lam_val) + calc = d.current_potential_energy().value() + assert calc == pytest.approx(nrg, abs=1e-4) diff --git a/tests/convert/test_openmm_minimise.py b/tests/convert/test_openmm_minimise.py index bb26f0687..9f7dc2045 100644 --- a/tests/convert/test_openmm_minimise.py +++ b/tests/convert/test_openmm_minimise.py @@ -23,10 +23,7 @@ def test_openmm_simple_minimise(ala_mols, openmm_platform): reason="openmm support is not available", ) def test_openmm_minimise_lambda(merged_ethane_methanol, openmm_platform): - mols = merged_ethane_methanol.clone() - - for mol in mols.molecules("molecule property is_perturbable"): - mols.update(mol.perturbation().link_to_reference().commit()) + mols = sr.morph.link_to_reference(merged_ethane_methanol) # this blows up because of incompatible exceptions/exclusions mol = ( diff --git a/tests/convert/test_openmm_restraints.py b/tests/convert/test_openmm_restraints.py index 3aeaf1832..7f2b5ee2e 100644 --- a/tests/convert/test_openmm_restraints.py +++ b/tests/convert/test_openmm_restraints.py @@ -137,7 +137,9 @@ def test_openmm_alchemical_restraints(ala_mols, openmm_platform): l = sr.cas.LambdaSchedule() l.add_stage("restraints", l.lam() * l.initial()) - l.set_equation("restraints", "restraint", l.lam() * l.initial()) + l.set_equation( + stage="restraints", lever="restraint", equation=l.lam() * l.initial() + ) d = mol.dynamics(timestep="1fs", restraints=restraints, schedule=l, map=map) @@ -203,14 +205,14 @@ def test_openmm_named_restraints(ala_mols, openmm_platform): l = sr.cas.LambdaSchedule() l.add_stage("1", 0) - l.set_equation("1", "positional", l.lam() * l.initial()) + l.set_equation(stage="1", lever="positional", equation=l.lam() * l.initial()) l.add_stage("2", 0) - l.set_equation("2", "distance", l.lam() * l.initial()) + l.set_equation(stage="2", lever="distance", equation=l.lam() * l.initial()) l.add_stage("3", 0) - l.set_equation("3", "positional", l.lam() * l.initial()) - l.set_equation("3", "distance", l.lam() * l.initial()) + l.set_equation(stage="3", lever="positional", equation=l.lam() * l.initial()) + l.set_equation(stage="3", lever="distance", equation=l.lam() * l.initial()) d = mol.dynamics(timestep="1fs", restraints=restraints, schedule=l, map=map) diff --git a/tests/morph/input/neopentane_methane.pert b/tests/morph/input/neopentane_methane.pert new file mode 100644 index 000000000..5acacaa39 --- /dev/null +++ b/tests/morph/input/neopentane_methane.pert @@ -0,0 +1,791 @@ +version 1 +molecule LIG + atom + name C1 + initial_type c3 + final_type du + initial_LJ 3.39967 0.10940 + final_LJ 0.00000 0.00000 + initial_charge -0.08534 + final_charge 0.00000 + endatom + atom + name C2 + initial_type c3 + final_type hc + initial_LJ 3.39967 0.10940 + final_LJ 2.64953 0.01570 + initial_charge -0.06024 + final_charge 0.02710 + endatom + atom + name C3 + initial_type c3 + final_type du + initial_LJ 3.39967 0.10940 + final_LJ 0.00000 0.00000 + initial_charge -0.08534 + final_charge 0.00000 + endatom + atom + name C4 + initial_type c3 + final_type c3 + initial_LJ 3.39967 0.10940 + final_LJ 3.39967 0.10940 + initial_charge -0.08534 + final_charge -0.10840 + endatom + atom + name C5 + initial_type c3 + final_type du + initial_LJ 3.39967 0.10940 + final_LJ 0.00000 0.00000 + initial_charge -0.08534 + final_charge 0.00000 + endatom + atom + name H10 + initial_type hc + final_type du + initial_LJ 2.64953 0.01570 + final_LJ 0.00000 0.00000 + initial_charge 0.03346 + final_charge 0.00000 + endatom + atom + name H11 + initial_type hc + final_type du + initial_LJ 2.64953 0.01570 + final_LJ 0.00000 0.00000 + initial_charge 0.03346 + final_charge 0.00000 + endatom + atom + name H12 + initial_type hc + final_type hc + initial_LJ 2.64953 0.01570 + final_LJ 2.64953 0.01570 + initial_charge 0.03346 + final_charge 0.02710 + endatom + atom + name H13 + initial_type hc + final_type hc + initial_LJ 2.64953 0.01570 + final_LJ 2.64953 0.01570 + initial_charge 0.03346 + final_charge 0.02710 + endatom + atom + name H14 + initial_type hc + final_type hc + initial_LJ 2.64953 0.01570 + final_LJ 2.64953 0.01570 + initial_charge 0.03346 + final_charge 0.02710 + endatom + atom + name H15 + initial_type hc + final_type du + initial_LJ 2.64953 0.01570 + final_LJ 0.00000 0.00000 + initial_charge 0.03346 + final_charge 0.00000 + endatom + atom + name H16 + initial_type hc + final_type du + initial_LJ 2.64953 0.01570 + final_LJ 0.00000 0.00000 + initial_charge 0.03346 + final_charge 0.00000 + endatom + atom + name H17 + initial_type hc + final_type du + initial_LJ 2.64953 0.01570 + final_LJ 0.00000 0.00000 + initial_charge 0.03346 + final_charge 0.00000 + endatom + atom + name H6 + initial_type hc + final_type du + initial_LJ 2.64953 0.01570 + final_LJ 0.00000 0.00000 + initial_charge 0.03346 + final_charge 0.00000 + endatom + atom + name H7 + initial_type hc + final_type du + initial_LJ 2.64953 0.01570 + final_LJ 0.00000 0.00000 + initial_charge 0.03346 + final_charge 0.00000 + endatom + atom + name H8 + initial_type hc + final_type du + initial_LJ 2.64953 0.01570 + final_LJ 0.00000 0.00000 + initial_charge 0.03346 + final_charge 0.00000 + endatom + atom + name H9 + initial_type hc + final_type du + initial_LJ 2.64953 0.01570 + final_LJ 0.00000 0.00000 + initial_charge 0.03346 + final_charge 0.00000 + endatom + bond + atom0 C1 + atom1 C2 + initial_force 300.90000 + initial_equil 1.53750 + final_force 300.90000 + final_equil 1.53750 + endbond + bond + atom0 C1 + atom1 H6 + initial_force 330.60000 + initial_equil 1.09690 + final_force 330.60000 + final_equil 1.09690 + endbond + bond + atom0 C1 + atom1 H7 + initial_force 330.60000 + initial_equil 1.09690 + final_force 330.60000 + final_equil 1.09690 + endbond + bond + atom0 C1 + atom1 H8 + initial_force 330.60000 + initial_equil 1.09690 + final_force 330.60000 + final_equil 1.09690 + endbond + bond + atom0 C2 + atom1 C3 + initial_force 300.90000 + initial_equil 1.53750 + final_force 300.90000 + final_equil 1.53750 + endbond + bond + atom0 C2 + atom1 C4 + initial_force 300.90000 + initial_equil 1.53750 + final_force 330.60000 + final_equil 1.09690 + endbond + bond + atom0 C2 + atom1 C5 + initial_force 300.90000 + initial_equil 1.53750 + final_force 300.90000 + final_equil 1.53750 + endbond + bond + atom0 C3 + atom1 H10 + initial_force 330.60000 + initial_equil 1.09690 + final_force 330.60000 + final_equil 1.09690 + endbond + bond + atom0 C3 + atom1 H11 + initial_force 330.60000 + initial_equil 1.09690 + final_force 330.60000 + final_equil 1.09690 + endbond + bond + atom0 C3 + atom1 H9 + initial_force 330.60000 + initial_equil 1.09690 + final_force 330.60000 + final_equil 1.09690 + endbond + bond + atom0 C5 + atom1 H15 + initial_force 330.60000 + initial_equil 1.09690 + final_force 330.60000 + final_equil 1.09690 + endbond + bond + atom0 C5 + atom1 H16 + initial_force 330.60000 + initial_equil 1.09690 + final_force 330.60000 + final_equil 1.09690 + endbond + bond + atom0 C5 + atom1 H17 + initial_force 330.60000 + initial_equil 1.09690 + final_force 330.60000 + final_equil 1.09690 + endbond + angle + atom0 C2 + atom1 C1 + atom2 H6 + initial_force 46.30000 + initial_equil 1.91637 + final_force 46.30000 + final_equil 1.91637 + endangle + angle + atom0 C2 + atom1 C1 + atom2 H7 + initial_force 46.30000 + initial_equil 1.91637 + final_force 46.30000 + final_equil 1.91637 + endangle + angle + atom0 C2 + atom1 C1 + atom2 H8 + initial_force 46.30000 + initial_equil 1.91637 + final_force 46.30000 + final_equil 1.91637 + endangle + angle + atom0 H6 + atom1 C1 + atom2 H7 + initial_force 39.40000 + initial_equil 1.87763 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 H6 + atom1 C1 + atom2 H8 + initial_force 39.40000 + initial_equil 1.87763 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 H7 + atom1 C1 + atom2 H8 + initial_force 39.40000 + initial_equil 1.87763 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 C1 + atom1 C2 + atom2 C3 + initial_force 62.90000 + initial_equil 1.94622 + final_force 62.90000 + final_equil 1.94622 + endangle + angle + atom0 C1 + atom1 C2 + atom2 C4 + initial_force 62.90000 + initial_equil 1.94622 + final_force 62.90000 + final_equil 1.94622 + endangle + angle + atom0 C1 + atom1 C2 + atom2 C5 + initial_force 62.90000 + initial_equil 1.94622 + final_force 62.90000 + final_equil 1.94622 + endangle + angle + atom0 C3 + atom1 C2 + atom2 C4 + initial_force 62.90000 + initial_equil 1.94622 + final_force 62.90000 + final_equil 1.94622 + endangle + angle + atom0 C3 + atom1 C2 + atom2 C5 + initial_force 62.90000 + initial_equil 1.94622 + final_force 62.90000 + final_equil 1.94622 + endangle + angle + atom0 C4 + atom1 C2 + atom2 C5 + initial_force 62.90000 + initial_equil 1.94622 + final_force 62.90000 + final_equil 1.94622 + endangle + angle + atom0 C2 + atom1 C3 + atom2 H10 + initial_force 46.30000 + initial_equil 1.91637 + final_force 46.30000 + final_equil 1.91637 + endangle + angle + atom0 C2 + atom1 C3 + atom2 H11 + initial_force 46.30000 + initial_equil 1.91637 + final_force 46.30000 + final_equil 1.91637 + endangle + angle + atom0 C2 + atom1 C3 + atom2 H9 + initial_force 46.30000 + initial_equil 1.91637 + final_force 46.30000 + final_equil 1.91637 + endangle + angle + atom0 H10 + atom1 C3 + atom2 H11 + initial_force 39.40000 + initial_equil 1.87763 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 H9 + atom1 C3 + atom2 H10 + initial_force 39.40000 + initial_equil 1.87763 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 H9 + atom1 C3 + atom2 H11 + initial_force 39.40000 + initial_equil 1.87763 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 C2 + atom1 C4 + atom2 H12 + initial_force 46.30000 + initial_equil 1.91637 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 C2 + atom1 C4 + atom2 H13 + initial_force 46.30000 + initial_equil 1.91637 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 C2 + atom1 C4 + atom2 H14 + initial_force 46.30000 + initial_equil 1.91637 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 C2 + atom1 C5 + atom2 H15 + initial_force 46.30000 + initial_equil 1.91637 + final_force 46.30000 + final_equil 1.91637 + endangle + angle + atom0 C2 + atom1 C5 + atom2 H16 + initial_force 46.30000 + initial_equil 1.91637 + final_force 46.30000 + final_equil 1.91637 + endangle + angle + atom0 C2 + atom1 C5 + atom2 H17 + initial_force 46.30000 + initial_equil 1.91637 + final_force 46.30000 + final_equil 1.91637 + endangle + angle + atom0 H15 + atom1 C5 + atom2 H16 + initial_force 39.40000 + initial_equil 1.87763 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 H15 + atom1 C5 + atom2 H17 + initial_force 39.40000 + initial_equil 1.87763 + final_force 39.40000 + final_equil 1.87763 + endangle + angle + atom0 H16 + atom1 C5 + atom2 H17 + initial_force 39.40000 + initial_equil 1.87763 + final_force 39.40000 + final_equil 1.87763 + endangle + dihedral + atom0 C3 + atom1 C2 + atom2 C1 + atom3 H6 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C3 + atom1 C2 + atom2 C1 + atom3 H7 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C3 + atom1 C2 + atom2 C1 + atom3 H8 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C4 + atom1 C2 + atom2 C1 + atom3 H6 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C4 + atom1 C2 + atom2 C1 + atom3 H7 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C4 + atom1 C2 + atom2 C1 + atom3 H8 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C5 + atom1 C2 + atom2 C1 + atom3 H6 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C5 + atom1 C2 + atom2 C1 + atom3 H7 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C5 + atom1 C2 + atom2 C1 + atom3 H8 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C1 + atom1 C2 + atom2 C3 + atom3 H10 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C1 + atom1 C2 + atom2 C3 + atom3 H11 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C1 + atom1 C2 + atom2 C3 + atom3 H9 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C4 + atom1 C2 + atom2 C3 + atom3 H10 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C4 + atom1 C2 + atom2 C3 + atom3 H11 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C4 + atom1 C2 + atom2 C3 + atom3 H9 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C5 + atom1 C2 + atom2 C3 + atom3 H10 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C5 + atom1 C2 + atom2 C3 + atom3 H11 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C5 + atom1 C2 + atom2 C3 + atom3 H9 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C1 + atom1 C2 + atom2 C4 + atom3 H12 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C1 + atom1 C2 + atom2 C4 + atom3 H13 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C1 + atom1 C2 + atom2 C4 + atom3 H14 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C3 + atom1 C2 + atom2 C4 + atom3 H12 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C3 + atom1 C2 + atom2 C4 + atom3 H13 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C3 + atom1 C2 + atom2 C4 + atom3 H14 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C5 + atom1 C2 + atom2 C4 + atom3 H12 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C5 + atom1 C2 + atom2 C4 + atom3 H13 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C5 + atom1 C2 + atom2 C4 + atom3 H14 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C1 + atom1 C2 + atom2 C5 + atom3 H15 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C1 + atom1 C2 + atom2 C5 + atom3 H16 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C1 + atom1 C2 + atom2 C5 + atom3 H17 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C3 + atom1 C2 + atom2 C5 + atom3 H15 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C3 + atom1 C2 + atom2 C5 + atom3 H16 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C3 + atom1 C2 + atom2 C5 + atom3 H17 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C4 + atom1 C2 + atom2 C5 + atom3 H15 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C4 + atom1 C2 + atom2 C5 + atom3 H16 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral + dihedral + atom0 C4 + atom1 C2 + atom2 C5 + atom3 H17 + initial_form 0.1600 3.0 -0.000000 + final_form 0.0000 3.0 -0.000000 + enddihedral +endmolecule diff --git a/tests/morph/test_pert.py b/tests/morph/test_pert.py new file mode 100644 index 000000000..535830194 --- /dev/null +++ b/tests/morph/test_pert.py @@ -0,0 +1,322 @@ +import sire as sr + +import pytest + +# get the directory of this script file +import os + +test_dir = os.path.dirname(os.path.realpath(__file__)) + +neopentane_methane_pert = os.path.join(test_dir, "input", "neopentane_methane.pert") + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_pertfile(neopentane_methane, openmm_platform): + mols = neopentane_methane.clone() + + map = { + "space": sr.vol.Cartesian(), + "platform": openmm_platform, + "constraint": "none", + "cutoff": "none", + } + + mols = sr.morph.link_to_reference(mols) + + # (the pert file has zeroed ghost torsions) + mols = sr.morph.zero_ghost_torsions(mols) + + mols2 = sr.morph.extract_reference(mols) + + with pytest.raises(KeyError): + assert len(mols2.molecules("property is_perturbable")) == 0 + + mols2.update(sr.morph.create_from_pertfile(mols2[0], neopentane_methane_pert)) + + assert len(mols2.molecules("property is_perturbable")) == 1 + + p1 = mols[0].perturbation().to_openmm(constraint="bonds") + p2 = mols2[0].perturbation().to_openmm(constraint="bonds") + + c_atm1 = p1.changed_atoms(to_pandas=False) + c_atm2 = p2.changed_atoms(to_pandas=False) + + assert len(c_atm1) == len(c_atm2) + + for atm1 in c_atm1: + found = False + for atm2 in c_atm2: + found = True + if atm1[0].name() == atm2[0].name(): + for i in range(1, len(atm1)): + assert atm1[i] == pytest.approx(atm2[i], abs=1e-3) + + break + + assert found + + c_bnd1 = p1.changed_bonds(to_pandas=False) + c_bnd2 = p2.changed_bonds(to_pandas=False) + + assert len(c_bnd1) == len(c_bnd2) + + for bnd1 in c_bnd1: + found = False + for bnd2 in c_bnd2: + found = True + if bnd1[0].id() == bnd2[0].id(): + for i in range(1, len(bnd1)): + assert bnd1[i] == pytest.approx(bnd2[i], abs=1e-3) + + break + + assert found + + c_ang1 = p1.changed_angles(to_pandas=False) + c_ang2 = p2.changed_angles(to_pandas=False) + + assert len(c_ang1) == len(c_ang2) + + for ang1 in c_ang1: + found = False + for ang2 in c_ang2: + found = True + if ang1[0].id() == ang2[0].id(): + for i in range(1, len(ang1)): + assert ang1[i] == pytest.approx(ang2[i], abs=1e-3) + + break + + assert found + + c_tor1 = p1.changed_torsions(to_pandas=False) + c_tor2 = p2.changed_torsions(to_pandas=False) + + assert len(c_tor1) == len(c_tor2) + + for tor1 in c_tor1: + found = False + for tor2 in c_tor2: + found = True + if tor1[0].id() == tor2[0].id(): + for i in range(1, len(tor1)): + assert tor1[i] == pytest.approx(tor2[i], abs=1e-3) + + break + + assert found + + c_con1 = p1.changed_constraints(to_pandas=False) + c_con2 = p2.changed_constraints(to_pandas=False) + + assert len(c_con1) == len(c_con2) + + for con1 in c_con1: + found = False + for con2 in c_con2: + found = True + if con1[0][0].name() == con2[0][0].name(): + for i in range(1, len(con1)): + assert con1[i] == pytest.approx(con2[i], abs=1e-3) + + break + + assert found + + d = mols.dynamics(lambda_value=0.0, map=map) + d2 = mols2.dynamics(lambda_value=0.0, map=map) + + assert d.current_potential_energy().value() == pytest.approx( + d2.current_potential_energy().value(), abs=1e-3 + ) + + d = mols.dynamics(lambda_value=1.0, map=map) + d2 = mols2.dynamics(lambda_value=1.0, map=map) + + assert d.current_potential_energy().value() == pytest.approx( + d2.current_potential_energy().value(), abs=1e-3 + ) + + d = mols.dynamics(lambda_value=0.5, map=map) + d2 = mols2.dynamics(lambda_value=0.5, map=map) + + assert d.current_potential_energy().value() == pytest.approx( + d2.current_potential_energy().value(), abs=1e-3 + ) + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_extract_and_link(neopentane_methane, openmm_platform): + mols = neopentane_methane.clone() + + map = { + "space": sr.vol.Cartesian(), + "platform": openmm_platform, + "constraint": "none", + "cutoff": "none", + } + + assert len(mols.molecules("property is_perturbable")) == 1 + + ref_mols = sr.morph.extract_reference(mols, remove_ghosts=False) + pert_mols = sr.morph.extract_perturbed(mols, remove_ghosts=False) + + with pytest.raises(KeyError): + assert len(ref_mols.molecules("property is_perturbable")) == 0 + + mols = sr.morph.link_to_reference(mols) + + nrg_0_0 = ( + mols.dynamics(ignore_perturbations=True, map=map) + .current_potential_energy() + .value() + ) + + nrg_1_0 = ( + mols.dynamics(swap_end_states=True, ignore_perturbations=True, map=map) + .current_potential_energy() + .value() + ) + + mols = sr.morph.link_to_perturbed(mols) + + nrg_0_1 = ( + mols.dynamics(ignore_perturbations=True, map=map) + .current_potential_energy() + .value() + ) + + nrg_1_1 = ( + mols.dynamics(swap_end_states=True, ignore_perturbations=True, map=map) + .current_potential_energy() + .value() + ) + + assert nrg_0_0 != nrg_1_0 + assert nrg_0_1 != nrg_1_1 + + assert ref_mols.energy().value() == pytest.approx(nrg_0_0, abs=1e-3) + assert pert_mols.energy().value() == pytest.approx(nrg_1_1, abs=1e-3) + + mols = sr.morph.link_to_reference(mols) + + assert nrg_0_0 == pytest.approx( + mols.dynamics(lambda_value=0.0, map=map).current_potential_energy().value(), + 1e-3, + ) + + assert nrg_1_0 == pytest.approx( + mols.dynamics(lambda_value=1.0, map=map).current_potential_energy().value(), + 1e-3, + ) + + for key in ref_mols[0].property_keys(): + assert mols[0].property(key) == ref_mols[0].property(key) + + nrg_ref = ref_mols.dynamics(map=map).current_potential_energy().value() + + assert nrg_0_0 == pytest.approx(nrg_ref, 1e-3) + + with pytest.raises(KeyError): + assert len(pert_mols.molecules("property is_perturbable")) == 0 + + mols = sr.morph.link_to_perturbed(mols) + + for key in ref_mols[0].property_keys(): + assert mols[0].property(key) == pert_mols[0].property(key) + + nrg_pert = pert_mols.dynamics(map=map).current_potential_energy().value() + + assert nrg_1_1 == pytest.approx(nrg_pert, 1e-3) + + +@pytest.mark.skipif( + "openmm" not in sr.convert.supported_formats(), + reason="openmm support is not available", +) +def test_extract_and_link_solv(solvated_neopentane_methane, openmm_platform): + mols = solvated_neopentane_methane.clone() + + map = { + "space": mols.space(), + "platform": openmm_platform, + "constraint": "none", + "cutoff_type": "RF", + "cutoff": "7.5A", + } + + assert len(mols.molecules("property is_perturbable")) == 1 + + ref_mols = sr.morph.extract_reference(mols, remove_ghosts=False) + pert_mols = sr.morph.extract_perturbed(mols, remove_ghosts=False) + + with pytest.raises(KeyError): + assert len(ref_mols.molecules("property is_perturbable")) == 0 + + mols = sr.morph.link_to_reference(mols) + + nrg_0_0 = ( + mols.dynamics(ignore_perturbations=True, map=map) + .current_potential_energy() + .value() + ) + + nrg_1_0 = ( + mols.dynamics(swap_end_states=True, ignore_perturbations=True, map=map) + .current_potential_energy() + .value() + ) + + mols = sr.morph.link_to_perturbed(mols) + + nrg_0_1 = ( + mols.dynamics(ignore_perturbations=True, map=map) + .current_potential_energy() + .value() + ) + + nrg_1_1 = ( + mols.dynamics(swap_end_states=True, ignore_perturbations=True, map=map) + .current_potential_energy() + .value() + ) + + assert nrg_0_0 != nrg_1_0 + assert nrg_0_1 != nrg_1_1 + + mols = sr.morph.link_to_reference(mols) + + assert nrg_0_0 == pytest.approx( + mols.dynamics(lambda_value=0.0, map=map).current_potential_energy().value(), + 1e-3, + ) + + assert nrg_1_0 == pytest.approx( + mols.dynamics(lambda_value=1.0, map=map).current_potential_energy().value(), + 1e-3, + ) + + for key in ref_mols[0].property_keys(): + assert mols[0].property(key) == ref_mols[0].property(key) + + nrg_ref = ref_mols.dynamics(map=map).current_potential_energy().value() + + assert nrg_0_0 == pytest.approx(nrg_ref, 1e-3) + + with pytest.raises(KeyError): + assert len(pert_mols.molecules("property is_perturbable")) == 0 + + mols = sr.morph.link_to_perturbed(mols) + + for key in ref_mols[0].property_keys(): + assert mols[0].property(key) == pert_mols[0].property(key) + + nrg_pert = pert_mols.dynamics(map=map).current_potential_energy().value() + + assert nrg_1_1 == pytest.approx(nrg_pert, 1e-3) diff --git a/tests/options/test_options.py b/tests/options/test_options.py index 17f46f0d2..ea1cefeb4 100644 --- a/tests/options/test_options.py +++ b/tests/options/test_options.py @@ -15,9 +15,7 @@ def create(option: str): @staticmethod def options(include_docs: bool = False): - return sr.options.Option._options( - TestOptions, include_docs=include_docs - ) + return sr.options.Option._options(TestOptions, include_docs=include_docs) assert TestOptions.A == "a" assert TestOptions.B == "b" @@ -52,6 +50,7 @@ def options(include_docs: bool = False): " H-bonds-h-ANGLES ", "h_bonds_h_angles", ), + (sr.options.Constraint, " H-Bonds_not_Perturbed", "h_bonds_not_perturbed"), (sr.options.PerturbableConstraint, "None", "none"), (sr.options.Cutoff, "Particle Mesh Ewald", "pme"), (sr.options.Cutoff, "REACTION FIELD", "rf"), diff --git a/tests/stream/test_stream_lever.py b/tests/stream/test_stream_lever.py new file mode 100644 index 000000000..e1d45b796 --- /dev/null +++ b/tests/stream/test_stream_lever.py @@ -0,0 +1,23 @@ +import sire as sr + +import pytest + + +def test_stream_lever(tmpdir): + dir = tmpdir.mkdir("test_stream_lever") + + s3file = str(dir.join("lever.s3")) + + l = sr.cas.LambdaSchedule.standard_morph() + + l.add_force("bond") + l.add_force("angle") + + l.add_lever("charge") + l.add_lever("sigma") + + sr.stream.save(l, s3file) + + l2 = sr.stream.load(s3file) + + assert l == l2 diff --git a/tests/units/test_parse_vector.py b/tests/units/test_parse_vector.py new file mode 100644 index 000000000..f95b524db --- /dev/null +++ b/tests/units/test_parse_vector.py @@ -0,0 +1,28 @@ +import pytest +import sire as sr + + +@pytest.mark.parametrize( + "args, expect", + [ + ([0], sr.maths.Vector(0)), + ([1, 2, 3], sr.maths.Vector(1, 2, 3)), + (["1 A", "2 A", "3 A"], sr.maths.Vector(1, 2, 3)), + ([1, 2, 3, "A"], sr.maths.Vector(1, 2, 3)), + ([sr.u("1 A"), sr.u("2 A"), sr.u("3 A")], sr.maths.Vector(1, 2, 3)), + ( + [3, 4, 5, "A ps-1"], + sr.legacy.Mol.Velocity3D("3 A ps-1", "4 A ps-1", "5 A ps-1"), + ), + ( + [3, 4, 5, "newton"], + sr.legacy.Mol.Force3D("3 newton", "4 newton", "5 newton"), + ), + ( + [1, 2, 3, "oC"], + (sr.u("1 oC"), sr.u("2 oC"), sr.u("3 oC")), + ), + ], +) +def test_pase_vector(args, expect): + assert sr.v(*args) == expect diff --git a/wrapper/AutoGenerate/create_wrappers.py b/wrapper/AutoGenerate/create_wrappers.py index d1ccda2a2..679c0f429 100644 --- a/wrapper/AutoGenerate/create_wrappers.py +++ b/wrapper/AutoGenerate/create_wrappers.py @@ -34,9 +34,10 @@ all_exposed_classes = {} + def demangle(c): """Internal function that does its best job to demangle a class into - a recognisable string""" + a recognisable string""" bases = [] base = c.parent @@ -51,39 +52,50 @@ def demangle(c): bases.append(c.name) return "::".join(bases) + def _generate_bases(self, base_creators): """This is a new version of the Py++ generate_bases function that only - adds in bases that are known to be exposed (known via the global list - of exposed classes)""" + adds in bases that are known to be exposed (known via the global list + of exposed classes)""" bases = [] - assert isinstance( self.declaration, declarations.class_t ) + assert isinstance(self.declaration, declarations.class_t) for base_desc in self.declaration.bases: - assert isinstance( base_desc, declarations.hierarchy_info_t ) + assert isinstance(base_desc, declarations.hierarchy_info_t) if base_desc.access != declarations.ACCESS_TYPES.PUBLIC: continue try: - #only include bases that are in the global list - if (base_desc.related_class.demangled in all_exposed_classes): - bases.append( algorithm.create_identifier( self, base_desc.related_class.decl_string ) ) + # only include bases that are in the global list + if base_desc.related_class.demangled in all_exposed_classes: + bases.append( + algorithm.create_identifier( + self, base_desc.related_class.decl_string + ) + ) except: # doesn't work with CastXML demangled = demangle(base_desc.related_class) - if (demangled in all_exposed_classes): - bases.append( algorithm.create_identifier( self, base_desc.related_class.decl_string ) ) + if demangled in all_exposed_classes: + bases.append( + algorithm.create_identifier( + self, base_desc.related_class.decl_string + ) + ) if not bases: return None - bases_identifier = algorithm.create_identifier( self, '::boost::python::bases' ) + bases_identifier = algorithm.create_identifier(self, "::boost::python::bases") + + return declarations.templates.join(bases_identifier, bases) - return declarations.templates.join( bases_identifier, bases ) class_t._generate_bases = _generate_bases + #### #### Override the free_function functions so that we fix a compile bug using xlC on AIX #### Overloaded function signatures output by Py++ look like this; @@ -104,120 +116,153 @@ def _generate_bases(self, base_creators): #### This compiles property using xlC. The below code changes free_function_t and #### mem_function_t to create the xlC compatible code, rather than the original Py++ code #### -def _create_function_type_alias_code( self, exported_class_alias=None ): +def _create_function_type_alias_code(self, exported_class_alias=None): f_type = self.declaration.function_type() falias = self.function_type_alias - fname = declarations.full_name( self.declaration, with_defaults=False ) - fvalue = re.sub("_type$", "_value", falias ) + fname = declarations.full_name(self.declaration, with_defaults=False) + fvalue = re.sub("_type$", "_value", falias) + + return "typedef %s;\n%s %s( &%s );" % ( + f_type.create_typedef(falias, with_defaults=False), + falias, + fvalue, + fname, + ) - return "typedef %s;\n%s %s( &%s );" % (f_type.create_typedef( falias, with_defaults=False ), - falias, fvalue, fname) free_function_t.create_function_type_alias_code = _create_function_type_alias_code mem_fun_t.create_function_type_alias_code = _create_function_type_alias_code + def _create_function_ref_code(self, use_function_alias=False): - fname = declarations.full_name( self.declaration, with_defaults=False ) + fname = declarations.full_name(self.declaration, with_defaults=False) if use_function_alias: falias = self.function_type_alias fvalue = re.sub("_type$", "_value", falias) return fvalue elif self.declaration.create_with_signature: - return '(%s)( &%s )' % ( self.declaration.function_type().partial_decl_string, fname ) + return "(%s)( &%s )" % ( + self.declaration.function_type().partial_decl_string, + fname, + ) else: - return '&%s' % fname + return "&%s" % fname + free_function_t.create_function_ref_code = _create_function_ref_code mem_fun_t.create_function_ref_code = _create_function_ref_code -#fix broken "operators" function -def operators( self, name=None, symbol=None, function=None, return_type=None, arg_types=None, decl_type=None, header_dir=None, header_file=None, recursive=None ): + +# fix broken "operators" function +def operators( + self, + name=None, + symbol=None, + function=None, + return_type=None, + arg_types=None, + decl_type=None, + header_dir=None, + header_file=None, + recursive=None, +): """Please see L{decl_wrappers.scopedef_t} class documentation""" - return self.global_ns.operators( name=name - , symbol=symbol - , function=function - , return_type=return_type - , arg_types=arg_types - , header_dir=header_dir - , header_file=header_file - , recursive=recursive ) + return self.global_ns.operators( + name=name, + symbol=symbol, + function=function, + return_type=return_type, + arg_types=arg_types, + header_dir=header_dir, + header_file=header_file, + recursive=recursive, + ) + module_builder_t.operators = operators + def has_datastream_operators(mb, c): - """Return whether or not the class has QDataStream streaming operators""" + """Return whether or not the class has QDataStream streaming operators""" - try: - d = mb.operators(arg_types=["::QDataStream &","%s &" % c.decl_string]) - return len(d) > 0 + try: + d = mb.operators(arg_types=["::QDataStream &", "%s &" % c.decl_string]) + return len(d) > 0 + + except: + return False - except: - return False def has_function(c, funcname): - """Recursively move through this class and its bases to find - if it has a function called 'funcname'""" + """Recursively move through this class and its bases to find + if it has a function called 'funcname'""" - try: - c.decl(funcname) - return True - except: + try: + c.decl(funcname) + return True + except: + for base in c.bases: + if has_function(base.related_class, funcname): + return True - for base in c.bases: - if has_function(base.related_class, funcname): - return True + return False - return False def find_class(mb, classname): - # replace Qt integers with C++ - classname = classname.replace("qint64", "long long") - - # replace '>>' template with '> >' - while classname.find(">>") != -1: - classname = classname.replace(">>", "> >") - - for clas in mb.classes(): - if str(clas).find("%s [class]" % classname) != -1 or \ - str(clas).find("%s [struct]" % classname) != -1 or \ - str(clas).find("%s [typedef]" % classname) != -1: - return clas - else: - for alias in clas.aliases: - if str(alias).find("%s [class]" % classname) != -1 or \ - str(alias).find("%s [struct]" % classname) != -1 or \ - str(alias).find("%s [typedef]" % classname) != -1: - return clas - - print("Cannot find the class %s" % classname) - raise TypeError("Cannot find the class %s" % classname) + # replace Qt integers with C++ + classname = classname.replace("qint64", "long long") + + # replace '>>' template with '> >' + while classname.find(">>") != -1: + classname = classname.replace(">>", "> >") + + for clas in mb.classes(): + if ( + str(clas).find("%s [class]" % classname) != -1 + or str(clas).find("%s [struct]" % classname) != -1 + or str(clas).find("%s [typedef]" % classname) != -1 + ): + return clas + else: + for alias in clas.aliases: + if ( + str(alias).find("%s [class]" % classname) != -1 + or str(alias).find("%s [struct]" % classname) != -1 + or str(alias).find("%s [typedef]" % classname) != -1 + ): + return clas + + print("Cannot find the class %s" % classname) + raise TypeError("Cannot find the class %s" % classname) + def export_function(mb, function, includes): - """Do all the work necessary to allow the function 'function' - to be exported, adding the header files in 'includes' - to the generated C++""" + """Do all the work necessary to allow the function 'function' + to be exported, adding the header files in 'includes' + to the generated C++""" + + name = function.split("::")[-1] + root = "::".join(function.split("::")[0:-1]) + "::" - name = function.split("::")[-1] - root = "::".join(function.split("::")[0:-1]) + "::" + try: + for f in mb.free_functions(name): + demangled = f.demangled - try: - for f in mb.free_functions(name): - demangled = f.demangled + if demangled: + if demangled.find(root) != -1: + f.include() - if demangled: - if demangled.find(root) != -1: - f.include() + for include in includes: + f.add_declaration_code("#include %s" % include) + except: + for f in mb.free_functions(name): + # demangled doesn't work with CastXML + if root.find(str(f.parent.name)) != -1: + f.include() - for include in includes: - f.add_declaration_code("#include %s" % include) - except: - for f in mb.free_functions(name): - # demangled doesn't work with CastXML - if root.find(str(f.parent.name)) != -1: - f.include() + for include in includes: + f.add_declaration_code("#include %s" % include) - for include in includes: - f.add_declaration_code("#include %s" % include) def has_clone_function(t): c = None @@ -248,11 +293,12 @@ def is_Index_T_(c): def fix_Index_T_(c): - """The Index_T_ classes need extra work to wrap... This function does that work""" - try: - c.operators("==").exclude() - except: - pass + """The Index_T_ classes need extra work to wrap... This function does that work""" + try: + c.operators("==").exclude() + except: + pass + has_copy_function = {} @@ -280,7 +326,7 @@ def is_copy_constructor(f): # we need something fuzzier for some templates. Here, if the # argument is called "other" and it ends with 'const &' then - # it is probably a copy constructor + # it is probably a copy constructor if decl_string.endswith(" const &") and arg.name == "other": return True @@ -290,19 +336,22 @@ def is_copy_constructor(f): def _call_with_release_gil(f): if f.call_policies is None or f.call_policies.is_default(): # we cannot hold the GIL for a function that has default arguments. - # This is because the default arguments will be deleted by python + # This is because the default arguments will be deleted by python # before the GIL is restored, leading to a crash for arg in f.arguments: if arg.default_value: - print(f"Skipping GIL for {f} as argument {arg} is not default. {arg.default_value}") + print( + f"Skipping GIL for {f} as argument {arg} is not default. {arg.default_value}" + ) return - f.call_policies = call_policies.custom_call_policies( "bp::release_gil_policy" ) + f.call_policies = call_policies.custom_call_policies("bp::release_gil_policy") + def call_all_with_released_gil(c): """Make sure that all functions in this class are called with - the gil released + the gil released """ try: funs = c.member_functions() @@ -312,6 +361,7 @@ def call_all_with_released_gil(c): for f in funs: _call_with_release_gil(f) + def call_with_released_gil(c, func_name): """Make sure that the gil is released when calling this function""" try: @@ -322,213 +372,230 @@ def call_with_released_gil(c, func_name): for f in funs: _call_with_release_gil(f) -def export_class(mb, classname, aliases, includes, special_code, auto_str_function=True): - """Do all the work necessary to allow the class called 'classname' - to be exported, using the supplied aliases, and using the - supplied special code, and adding the header files in 'includes' - to the generated C++""" - - #find the class in the declarations - c = find_class(mb, classname) - - #include the class in the wrapper - c.include() - - #write out all function signatures - c.calldefs().create_with_signature = True - c.always_expose_using_scope = True - - #add the extra include files for this class - for include_file in includes: - c.add_declaration_code("#include %s" % include_file) - - #ensure that the list of bases includes *all* bases, - # - this is to fix problems with typeerror being - # thrown for derived types - c.bases = c.recursive_bases - - #exclude any "clone" functions - try: - c.decls( "clone" ).exclude() - except: - pass - - #now replace copy_const_reference with clone_const_reference for suitable classes - funs = [] - - try: - #all copy_const_reference call policies with clone_const_reference - #funs = c.mem_funs( lambda f: declarations.is_reference( f.return_type ) ) - funs = c.member_functions( lambda f: f.return_type.decl_string.endswith("&") ) - except: - pass - - for f in funs: - if has_clone_function(f.return_type): - f.call_policies = call_policies.custom_call_policies( \ - "bp::return_value_policy", \ - "Helpers/clone_const_reference.hpp" ) - - #also add any operator[] or operator() functions - try: - #funs = c.operators( lambda f: declarations.is_reference( f.return_type ) ) - funs = c.operators( lambda f: f.return_type.decl_string.endswith("&") ) - except: - pass - - for f in funs: - if (str(f).find("[]") != -1) or (str(f).find("()") != -1): - if has_clone_function(f.return_type): - f.call_policies = call_policies.custom_call_policies( \ - "bp::return_value_policy", \ - "Helpers/clone_const_reference.hpp" ) - - #remove any declarations that return a pointer to something - #(special code is needed in these cases!) - for decl in c.decls(): - try: - if str(decl.return_type) != "char const *": - rt = str(decl.return_type) - if rt.endswith("*") or \ - rt.endswith("::iterator") or rt.endswith("::const_iterator"): - decl.exclude() - except: - pass - - try: - for o in c.operators(): - if o.call_policies is None: - o.exclude() - except: - pass - - #run any class specific code - fixed_classname = classname - - # replace '>>' template with '> >' - while fixed_classname.find(">>") != -1: - fixed_classname = fixed_classname.replace(">>", "> >") - - if (fixed_classname in special_code): - special_code[fixed_classname](c) - - #if this is a noncopyable class then remove all constructors! - if c.noncopyable: - c.constructors().exclude() - else: - #if there is a copy-constructor then ensure that - #it is exposed! - decls = c.decls() - - made_copy_function = False - - for decl in decls: - if made_copy_function: - break - - try: - if is_copy_constructor(decl): - #create a __copy__ function - class_name = re.sub(r"\s\[class\]","",str(c)) - class_name = re.sub(r"\s\[struct\]","",class_name) - - if not (class_name in has_copy_function): - has_copy_function[class_name] = True - - made_copy_function = True - - c.add_declaration_code( \ - "%s __copy__(const %s &other){ return %s(other); }" \ - % (class_name, class_name, class_name) ) - - c.add_registration_code( "def( \"__copy__\", &__copy__)" ) - - c.add_registration_code( "def( \"__deepcopy__\", &__copy__)" ) - c.add_registration_code( "def( \"clone\", &__copy__)" ) - - #only do this once for the class - break - - except AttributeError: - pass - - #If this is an Index_T_ class then fix the operators - if is_Index_T_(c): - fix_Index_T_(c) - - #if this class can be streamed to a QDataStream then add - #streaming operators - if has_datastream_operators(mb,c): - c.add_declaration_code( "#include \"Qt/qdatastream.hpp\"" ) + +def export_class( + mb, classname, aliases, includes, special_code, auto_str_function=True +): + """Do all the work necessary to allow the class called 'classname' + to be exported, using the supplied aliases, and using the + supplied special code, and adding the header files in 'includes' + to the generated C++""" + + # find the class in the declarations + c = find_class(mb, classname) + + # include the class in the wrapper + c.include() + + # write out all function signatures + c.calldefs().create_with_signature = True + c.always_expose_using_scope = True + + # add the extra include files for this class + for include_file in includes: + c.add_declaration_code("#include %s" % include_file) + + # ensure that the list of bases includes *all* bases, + # - this is to fix problems with typeerror being + # thrown for derived types + c.bases = c.recursive_bases + + # exclude any "clone" functions + try: + c.decls("clone").exclude() + except: + pass + + # now replace copy_const_reference with clone_const_reference for suitable classes + funs = [] + + try: + # all copy_const_reference call policies with clone_const_reference + # funs = c.mem_funs( lambda f: declarations.is_reference( f.return_type ) ) + funs = c.member_functions(lambda f: f.return_type.decl_string.endswith("&")) + except: + pass + + for f in funs: + if has_clone_function(f.return_type): + f.call_policies = call_policies.custom_call_policies( + "bp::return_value_policy", + "Helpers/clone_const_reference.hpp", + ) + + # also add any operator[] or operator() functions + try: + # funs = c.operators( lambda f: declarations.is_reference( f.return_type ) ) + funs = c.operators(lambda f: f.return_type.decl_string.endswith("&")) + except: + pass + + for f in funs: + if (str(f).find("[]") != -1) or (str(f).find("()") != -1): + if has_clone_function(f.return_type): + f.call_policies = call_policies.custom_call_policies( + "bp::return_value_policy", + "Helpers/clone_const_reference.hpp", + ) + + # remove any declarations that return a pointer to something + # (special code is needed in these cases!) + for decl in c.decls(): + try: + if str(decl.return_type) != "char const *": + rt = str(decl.return_type) + if ( + rt.endswith("*") + or rt.endswith("::iterator") + or rt.endswith("::const_iterator") + ): + decl.exclude() + except: + pass + + try: + for o in c.operators(): + if o.call_policies is None: + o.exclude() + except: + pass + + # run any class specific code + fixed_classname = classname + + # replace '>>' template with '> >' + while fixed_classname.find(">>") != -1: + fixed_classname = fixed_classname.replace(">>", "> >") + + if fixed_classname in special_code: + special_code[fixed_classname](c) + + # if this is a noncopyable class then remove all constructors! + if c.noncopyable: + c.constructors().exclude() + else: + # if there is a copy-constructor then ensure that + # it is exposed! + decls = c.decls() + + made_copy_function = False + + for decl in decls: + if made_copy_function: + break + + try: + if is_copy_constructor(decl): + # create a __copy__ function + class_name = re.sub(r"\s\[class\]", "", str(c)) + class_name = re.sub(r"\s\[struct\]", "", class_name) + + if not (class_name in has_copy_function): + has_copy_function[class_name] = True + + made_copy_function = True + + c.add_declaration_code( + "%s __copy__(const %s &other){ return %s(other); }" + % (class_name, class_name, class_name) + ) + + c.add_registration_code('def( "__copy__", &__copy__)') + + c.add_registration_code('def( "__deepcopy__", &__copy__)') + c.add_registration_code('def( "clone", &__copy__)') + + # only do this once for the class + break + + except AttributeError: + pass + + # If this is an Index_T_ class then fix the operators + if is_Index_T_(c): + fix_Index_T_(c) + + # if this class can be streamed to a QDataStream then add + # streaming operators + if has_datastream_operators(mb, c): + c.add_declaration_code('#include "Qt/qdatastream.hpp"') c.add_registration_code( """def( \"__rlshift__\", &__rlshift__QDataStream< %s >, - bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() )""" % c.decl_string ) + bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() )""" + % c.decl_string + ) c.add_registration_code( """def( \"__rrshift__\", &__rrshift__QDataStream< %s >, - bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() )""" % c.decl_string ) + bp::return_internal_reference<1, bp::with_custodian_and_ward<1,2> >() )""" + % c.decl_string + ) c.add_registration_code( - """def_pickle(sire_pickle_suite< %s >())""" % c.decl_string ) + """def_pickle(sire_pickle_suite< %s >())""" % c.decl_string + ) - #is there a "toString" function for this class? - if auto_str_function: - if has_function(c, "toString"): - #there is a .toString() member function - we can thus use the - #templer __str__ function provided in the Helpers directory - c.add_declaration_code( "#include \"Helpers/str.hpp\"" ) + # is there a "toString" function for this class? + if auto_str_function: + if has_function(c, "toString"): + # there is a .toString() member function - we can thus use the + # templer __str__ function provided in the Helpers directory + c.add_declaration_code('#include "Helpers/str.hpp"') - c.add_registration_code( "def( \"__str__\", &__str__< %s > )" % c.decl_string ) - c.add_registration_code( "def( \"__repr__\", &__str__< %s > )" % c.decl_string ) + c.add_registration_code('def( "__str__", &__str__< %s > )' % c.decl_string) + c.add_registration_code('def( "__repr__", &__str__< %s > )' % c.decl_string) - else: - #there is no .toString() function - # - instead create a new __str__ that just returns a pretty form - # of the name of the class - name = str(c.decl_string) + else: + # there is no .toString() function + # - instead create a new __str__ that just returns a pretty form + # of the name of the class + name = str(c.decl_string) + + if name.startswith("::"): + name = name[2:] - if name.startswith("::"): - name = name[2:] + c.add_declaration_code( + 'const char* pvt_get_name(const %s&){ return "%s";}' % (name, name) + ) - c.add_declaration_code( "const char* pvt_get_name(const %s&){ return \"%s\";}" % (name,name) ) + c.add_registration_code('def( "__str__", &pvt_get_name)') + c.add_registration_code('def( "__repr__", &pvt_get_name)') - c.add_registration_code("def( \"__str__\", &pvt_get_name)") - c.add_registration_code("def( \"__repr__\", &pvt_get_name)") + # call all functions while releasing the gil + call_all_with_released_gil(c) - # call all functions while releasing the gil - call_all_with_released_gil(c) + c.add_declaration_code('#include "Helpers/release_gil_policy.hpp"') - c.add_declaration_code( "#include \"Helpers/release_gil_policy.hpp\"" ) + # is there a "count" or "size" function for this class? + if has_function(c, "size"): + c.add_declaration_code('#include "Helpers/len.hpp"') + c.add_registration_code('def( "__len__", &__len_size< %s > )' % c.decl_string) + elif has_function(c, "count"): + c.add_declaration_code('#include "Helpers/len.hpp"') + c.add_registration_code('def( "__len__", &__len_count< %s > )' % c.decl_string) - #is there a "count" or "size" function for this class? - if has_function(c, "size"): - c.add_declaration_code( "#include \"Helpers/len.hpp\"" ) - c.add_registration_code("def( \"__len__\", &__len_size< %s > )" % c.decl_string ) - elif has_function(c, "count"): - c.add_declaration_code( "#include \"Helpers/len.hpp\"" ) - c.add_registration_code("def( \"__len__\", &__len_count< %s > )" % c.decl_string ) + # is there a python-style getitem function? + if has_function(c, "getitem"): + c.add_registration_code('def( "__getitem__", &%s::getitem )' % c.decl_string) - #is there a python-style getitem function? - if has_function(c, "getitem"): - c.add_registration_code("def( \"__getitem__\", &%s::getitem )" % c.decl_string ) + # is there a .hash() function? + if has_function(c, "hash"): + c.add_registration_code('def( "__hash__", &%s::hash )' % c.decl_string) - #is there a .hash() function? - if has_function(c, "hash"): - c.add_registration_code("def( \"__hash__\", &%s::hash )" % c.decl_string ) + # provide an alias for this class + if classname in aliases: + alias = aliases[classname] + if "::" in alias: + alias = "".join(alias.split("::")[1:]) - #provide an alias for this class - if (classname in aliases): - alias = aliases[classname] - if "::" in alias: - alias = "".join(alias.split("::")[1:]) + c.alias = alias - c.alias = alias def register_implicit_conversions(mb, implicitly_convertible): """This function sets the wrapper generator to use only the implicit conversions - that have been specifically specified in 'implicitly_convertible'""" + that have been specifically specified in 'implicitly_convertible'""" - #remove all existing implicit conversions + # remove all existing implicit conversions try: mb.constructors().allow_implicit_conversion = False except: @@ -539,117 +606,128 @@ def register_implicit_conversions(mb, implicitly_convertible): except: pass - #add our manual implicit conversions to the declaration section + # add our manual implicit conversions to the declaration section for conversion in implicitly_convertible: - mb.add_registration_code("bp::implicitly_convertible< %s, %s >();" % conversion) + mb.add_registration_code("bp::implicitly_convertible< %s, %s >();" % conversion) -def write_wrappers(mb, module, huge_classes, header_files = [], - source_docs = {} ): - """This function performs the actual work of writing the wrappers to disk""" - #make sure that the protected and private member functions and - #data aren't wrapped - try: - mb.calldefs( access_type_matcher_t( 'protected' ) ).exclude() - except: - pass +def write_wrappers(mb, module, huge_classes, header_files=[], source_docs={}): + """This function performs the actual work of writing the wrappers to disk""" - try: - mb.calldefs( access_type_matcher_t( 'private' ) ).exclude() - except: - pass + # make sure that the protected and private member functions and + # data aren't wrapped + try: + mb.calldefs(access_type_matcher_t("protected")).exclude() + except: + pass - #build a code creator - this must be done after the above, as - #otherwise our modifications above won't take effect - mb.build_code_creator( module_name="_%s" % module, - doc_extractor=doxygen_doc_extractor(source_docs) ) + try: + mb.calldefs(access_type_matcher_t("private")).exclude() + except: + pass + + # build a code creator - this must be done after the above, as + # otherwise our modifications above won't take effect + mb.build_code_creator( + module_name="_%s" % module, doc_extractor=doxygen_doc_extractor(source_docs) + ) + + # get rid of the standard headers + mb.code_creator.replace_included_headers(header_files) - #get rid of the standard headers - mb.code_creator.replace_included_headers( header_files ) + # give each piece of code the GPL license header + mb.code_creator.license = "// (C) Christopher Woods, GPL >= 3 License\n" - #give each piece of code the GPL license header - mb.code_creator.license = "// (C) Christopher Woods, GPL >= 3 License\n" + # use local directory paths + mb.code_creator.user_defined_directories.append(".") - #use local directory paths - mb.code_creator.user_defined_directories.append(".") + mb.split_module(".", huge_classes) - mb.split_module( ".", huge_classes ) def needPropertyWrappers(active_headers): - for header in active_headers: - if active_headers[header].hasProperties(): - return True + for header in active_headers: + if active_headers[header].hasProperties(): + return True + + return False - return False def writePropertyWrappers(mb, sourcedir, active_headers): - """This function writes the property wrappers that are required for this module""" + """This function writes the property wrappers that are required for this module""" + + # are there any wrappers? + if not needPropertyWrappers(active_headers): + return - #are there any wrappers? - if not needPropertyWrappers(active_headers): - return + # create the files + FILE = open("%s_properties.h" % sourcedir, "w") - #create the files - FILE = open("%s_properties.h" % sourcedir, "w") + print("#ifndef %s_PROPERTIES_H" % sourcedir, file=FILE) + print("#define %s_PROPERTIES_H\n" % sourcedir, file=FILE) + print("void register_%s_properties();\n" % sourcedir, file=FILE) + print("#endif", file=FILE) - print("#ifndef %s_PROPERTIES_H" % sourcedir, file=FILE) - print("#define %s_PROPERTIES_H\n" % sourcedir, file=FILE) - print("void register_%s_properties();\n" % sourcedir, file=FILE) - print("#endif", file=FILE) + FILE.close() - FILE.close() + FILE = open("%s_properties.cpp" % sourcedir, "w") - FILE = open("%s_properties.cpp" % sourcedir, "w") + print("#include ", file=FILE) + print("#include \n", file=FILE) + print('#include "Base/convertproperty.hpp"', file=FILE) + print('#include "%s_properties.h"\n' % sourcedir, file=FILE) - print("#include ", file=FILE) - print("#include \n", file=FILE) - print("#include \"Base/convertproperty.hpp\"", file=FILE) - print("#include \"%s_properties.h\"\n" % sourcedir, file=FILE) + for header in active_headers: + active_header = active_headers[header] + if active_header.hasProperties(): + for dependency in active_header.dependencies(): + print("#include %s" % dependency, file=FILE) - for header in active_headers: - active_header = active_headers[header] - if active_header.hasProperties(): - for dependency in active_header.dependencies(): - print("#include %s" % dependency, file=FILE) + print("void register_%s_properties()" % sourcedir, file=FILE) + print("{", file=FILE) - print("void register_%s_properties()" % sourcedir, file=FILE) - print("{", file=FILE) + for header in active_headers: + active_header = active_headers[header] + if active_header.hasProperties(): + for property in active_header.properties(): + print( + " register_property_container< %s, %s >();" + % (property[0], property[1]), + file=FILE, + ) - for header in active_headers: - active_header = active_headers[header] - if active_header.hasProperties(): - for property in active_header.properties(): - print(" register_property_container< %s, %s >();" % (property[0], property[1]), file=FILE) + print("}", file=FILE) - print("}", file=FILE) + FILE.close() - FILE.close() + mb.add_declaration_code('#include "%s_properties.h"' % sourcedir) + mb.add_registration_code("register_%s_properties();" % sourcedir) - mb.add_declaration_code("#include \"%s_properties.h\"" % sourcedir) - mb.add_registration_code("register_%s_properties();" % sourcedir) def fixMB(mb): - pass + pass -if __name__ == "__main__": - #read in the information about this module +if __name__ == "__main__": + # read in the information about this module lines = open("module_info", "r").readlines() module = lines[0].split()[1] sourcedir = lines[1].split()[1] rootdir = lines[2].split()[1] - #load up the dictionary of all exposed classes - all_exposed_classes = pickle.load( open("../classdb.data", "rb") ) + # load up the dictionary of all exposed classes + try: + all_exposed_classes = pickle.load(open("../classdb.data", "rb")) + except Exception: + all_exposed_classes = pickle.load(open("./classdb.data", "rb")) - #load up the active headers object - active_headers = pickle.load( open("active_headers.data", "rb") ) + # load up the active headers object + active_headers = pickle.load(open("active_headers.data", "rb")) - #load up all of the source-level documentation - source_docs = pickle.load( open("docs.data", "rb") ) + # load up all of the source-level documentation + source_docs = pickle.load(open("docs.data", "rb")) - #get the special code, big classes and implicit conversions + # get the special code, big classes and implicit conversions implicitly_convertible = [] special_code = {} huge_classes = [] @@ -658,7 +736,7 @@ def fixMB(mb): sys.path.append(".") from special_code import * - sire_include_dirs = [ rootdir, "%s/%s" % (rootdir,sourcedir) ] + sire_include_dirs = [rootdir, "%s/%s" % (rootdir, sourcedir)] # All of the headers must be installed in the sire.app/include directory dir = os.path.dirname(sys.executable) @@ -670,30 +748,36 @@ def fixMB(mb): need_input = False - if (qtdir is None): - print("You must set the environmental variable QTDIR to the location " + \ - "of the Qt4 header files") + if qtdir is None: + print( + "You must set the environmental variable QTDIR to the location " + + "of the Qt4 header files" + ) need_input = True - if (boostdir is None): - print("You must set the environmental variable BOOSTDIR to the location " + \ - "of the boost header files") + if boostdir is None: + print( + "You must set the environmental variable BOOSTDIR to the location " + + "of the boost header files" + ) need_input = True - if (gsldir is None): - print("You must set the environmental variable GSLDIR to the location " + \ - "of the GSL header files") + if gsldir is None: + print( + "You must set the environmental variable GSLDIR to the location " + + "of the GSL header files" + ) need_input = True - if (need_input): + if need_input: print("Cannot continue as I don't know where the header files are") sys.exit(-1) qt_include_dirs = [] - qt_include_dirs = [ qtdir, "%s/QtCore" % qtdir ] - boost_include_dirs = [ boostdir ] - gsl_include_dirs = [ gsldir ] + qt_include_dirs = [qtdir, "%s/QtCore" % qtdir] + boost_include_dirs = [boostdir] + gsl_include_dirs = [gsldir] generator_path, generator_name = pygccxml.utils.find_xml_generator() @@ -704,72 +788,86 @@ def fixMB(mb): print("Generating wrappers including OpenMM from %s" % openmm_include_dir) openmm_include_dirs = [openmm_include_dir] else: - print("Cannot find %s/OpenMM.h - disabling generation of OpenMM wrappers." % openmm_include_dir) + print( + "Cannot find %s/OpenMM.h - disabling generation of OpenMM wrappers." + % openmm_include_dir + ) openmm_include_dirs = None if os.getenv("VERBOSE"): pygccxml.utils.loggers.cxx_parser.setLevel(logging.DEBUG) if openmm_include_dirs is None: - #construct a module builder that will build all of the wrappers for this module + # construct a module builder that will build all of the wrappers for this module xml_generator_config = pygccxml.parser.xml_generator_configuration_t( - xml_generator_path=generator_path, - xml_generator=generator_name, - compiler="gcc", - cflags = "-m64 -fPIC -std=c++14", - include_paths = sire_include_dirs + qt_include_dirs + - boost_include_dirs + gsl_include_dirs, - define_symbols = ["GCCXML_PARSE", "__PIC__", - "SIRE_ALWAYS_INLINE=inline", - "SIRE_SKIP_INLINE_FUNCTIONS", - "SIREN_SKIP_INLINE_FUNCTIONS", - "SIRE_INSTANTIATE_TEMPLATES", - "SIREN_INSTANTIATE_TEMPLATES"] - ) - - mb = module_builder_t( files = [ "active_headers.h" ], - gccxml_config=xml_generator_config ) + xml_generator_path=generator_path, + xml_generator=generator_name, + compiler="gcc", + cflags="-m64 -fPIC -std=c++14", + include_paths=sire_include_dirs + + qt_include_dirs + + boost_include_dirs + + gsl_include_dirs, + define_symbols=[ + "GCCXML_PARSE", + "__PIC__", + "SIRE_ALWAYS_INLINE=inline", + "SIRE_SKIP_INLINE_FUNCTIONS", + "SIREN_SKIP_INLINE_FUNCTIONS", + "SIRE_INSTANTIATE_TEMPLATES", + "SIREN_INSTANTIATE_TEMPLATES", + ], + ) + + mb = module_builder_t( + files=["active_headers.h"], gccxml_config=xml_generator_config + ) else: - #construct a module builder that will build all of the wrappers for this module + # construct a module builder that will build all of the wrappers for this module xml_generator_config = pygccxml.parser.xml_generator_configuration_t( - xml_generator_path=generator_path, - xml_generator=generator_name, - compiler="gcc", - cflags = "-m64 -fPIC -std=c++14", - include_paths = sire_include_dirs + qt_include_dirs + - boost_include_dirs + gsl_include_dirs + - openmm_include_dirs, - define_symbols = ["GCCXML_PARSE", "__PIC__", - "SIRE_USE_OPENMM", - "SIRE_ALWAYS_INLINE=inline", - "SIRE_SKIP_INLINE_FUNCTIONS", - "SIREN_SKIP_INLINE_FUNCTIONS", - "SIRE_INSTANTIATE_TEMPLATES", - "SIREN_INSTANTIATE_TEMPLATES"] - ) - - mb = module_builder_t( files = [ "active_headers.h" ], - gccxml_config=xml_generator_config ) - - - #get rid of all virtual python functions - this is to stop slow wrapper code - #from being generated for C++ virtual objects + xml_generator_path=generator_path, + xml_generator=generator_name, + compiler="gcc", + cflags="-m64 -fPIC -std=c++14", + include_paths=sire_include_dirs + + qt_include_dirs + + boost_include_dirs + + gsl_include_dirs + + openmm_include_dirs, + define_symbols=[ + "GCCXML_PARSE", + "__PIC__", + "SIRE_USE_OPENMM", + "SIRE_ALWAYS_INLINE=inline", + "SIRE_SKIP_INLINE_FUNCTIONS", + "SIREN_SKIP_INLINE_FUNCTIONS", + "SIRE_INSTANTIATE_TEMPLATES", + "SIREN_INSTANTIATE_TEMPLATES", + ], + ) + + mb = module_builder_t( + files=["active_headers.h"], gccxml_config=xml_generator_config + ) + + # get rid of all virtual python functions - this is to stop slow wrapper code + # from being generated for C++ virtual objects for calldef in mb.calldefs(): try: calldef.virtuality = declarations.VIRTUALITY_TYPES.NOT_VIRTUAL except: pass - #add calls to additional hand-written code + # add calls to additional hand-written code if os.path.exists("%s_containers.h" % sourcedir): - mb.add_declaration_code( "#include \"%s_containers.h\"" % sourcedir ) - mb.add_registration_code( "register_%s_containers();" % sourcedir, tail=False ) + mb.add_declaration_code('#include "%s_containers.h"' % sourcedir) + mb.add_registration_code("register_%s_containers();" % sourcedir, tail=False) mb.calldefs().create_with_signature = True metaheaders = [] - #export each of the classes in this module in turn + # export each of the classes in this module in turn for header in active_headers: classes = active_headers[header].classes() includes = active_headers[header].dependencies() @@ -784,15 +882,17 @@ def fixMB(mb): export_function(mb, func, includes) if len(active_headers[header].metaTypes()) > 0: - metaheaders.append(header) + metaheaders.append(header) if len(metaheaders) > 0: - mb.add_declaration_code( "#include \"%s_registrars.h\"" % sourcedir ) - mb.add_registration_code( "register_%s_objects();" % sourcedir, tail=False ) + mb.add_declaration_code('#include "%s_registrars.h"' % sourcedir) + mb.add_registration_code("register_%s_objects();" % sourcedir, tail=False) FILE = open("%s_registrars.h" % sourcedir, "w") - print(r"//WARNING - AUTOGENERATED FILE - CONTENTS WILL BE OVERWRITTEN!", file=FILE) + print( + r"//WARNING - AUTOGENERATED FILE - CONTENTS WILL BE OVERWRITTEN!", file=FILE + ) print("#ifndef PYWRAP_%s_REGISTRARS_H" % sourcedir, file=FILE) print("#define PYWRAP_%s_REGISTRARS_H" % sourcedir, file=FILE) print("void register_%s_objects();" % sourcedir, file=FILE) @@ -801,14 +901,16 @@ def fixMB(mb): FILE = open("%s_registrars.cpp" % sourcedir, "w") - print(r"//WARNING - AUTOGENERATED FILE - CONTENTS WILL BE OVERWRITTEN!", file=FILE) + print( + r"//WARNING - AUTOGENERATED FILE - CONTENTS WILL BE OVERWRITTEN!", file=FILE + ) print("#include \n", file=FILE) - print("#include \"%s_registrars.h\"\n" % sourcedir, file=FILE) + print('#include "%s_registrars.h"\n' % sourcedir, file=FILE) for header in metaheaders: - print("#include \"%s\"" % header, file=FILE) + print('#include "%s"' % header, file=FILE) - print("\n#include \"Helpers/objectregistry.hpp\"\n", file=FILE) + print('\n#include "Helpers/objectregistry.hpp"\n', file=FILE) print("void register_%s_objects()\n{\n" % sourcedir, file=FILE) @@ -816,30 +918,33 @@ def fixMB(mb): metatypes = active_headers[header].metaTypes() for metatype in metatypes: - print(" ObjectRegistry::registerConverterFor< %s >();" % metatype, file=FILE) + print( + " ObjectRegistry::registerConverterFor< %s >();" % metatype, + file=FILE, + ) print("\n}\n", file=FILE) FILE.close() - #now export all of the namespace-level operators + # now export all of the namespace-level operators for operator in mb.operators(): p = str(operator.parent) if p.find(module) != -1 and p.find("[namespace]") != -1: operator.include() - #write the code that wraps up the Property classes + # write the code that wraps up the Property classes writePropertyWrappers(mb, sourcedir, active_headers) - #remove all implicit implicit conversions and add the explicit implicit conversions (!) + # remove all implicit implicit conversions and add the explicit implicit conversions (!) register_implicit_conversions(mb, implicitly_convertible) - #now perform any last-minute fixes + # now perform any last-minute fixes fixMB(mb) write_wrappers(mb, module, huge_classes, source_docs=source_docs) - #now write a CMakeFile that contains all of the autogenerated files + # now write a CMakeFile that contains all of the autogenerated files FILE = open("CMakeAutogenFile.txt", "w") print("# WARNING - AUTOGENERATED FILE - CONTENTS WILL BE OVERWRITTEN!", file=FILE) @@ -862,4 +967,3 @@ def fixMB(mb): print(" )", file=FILE) FILE.close() - diff --git a/wrapper/AutoGenerate/scanheaders.py b/wrapper/AutoGenerate/scanheaders.py index 9f77e035c..cbe18397c 100644 --- a/wrapper/AutoGenerate/scanheaders.py +++ b/wrapper/AutoGenerate/scanheaders.py @@ -14,19 +14,20 @@ from glob import glob + def getFiles(dir, pattern): - files = glob("%s/%s" % (dir,pattern)) + files = glob("%s/%s" % (dir, pattern)) trimmed_files = [] for file in files: - trimmed_files.append( file[len(dir)+1:] ) + trimmed_files.append(file[len(dir) + 1 :]) return trimmed_files -def getIncludes(file, lines): - includes = { "\"%s\"" % file : 1 } +def getIncludes(file, lines): + includes = {'"%s"' % file: 1} for line in lines: if line.find("CONDITIONAL_INCLUDE") == -1: @@ -39,31 +40,33 @@ def getIncludes(file, lines): ret.sort() return ret + def getDependencies(dir, file): """Return the list of header files included by the .cpp or .c file - that corresponds to 'file'""" + that corresponds to 'file'""" try: - #is there a corresponding .cpp file? + # is there a corresponding .cpp file? file_cpp = re.sub(r"h(p*)$", "cpp", file) - lines = open("%s/%s" % (dir,file_cpp), "r").readlines() + lines = open("%s/%s" % (dir, file_cpp), "r").readlines() return getIncludes(file, lines) except: pass try: - #is there a corresponding .c file? + # is there a corresponding .c file? file_c = re.sub(r"h(p*)$", "c", file) - lines = open("%s/%s" % (dir,file_c), "r").readlines() + lines = open("%s/%s" % (dir, file_c), "r").readlines() return getIncludes(file, lines) except: pass - return [ "\"%s\"" % file ] + return ['"%s"' % file] + class Properties: def __init__(self): @@ -71,13 +74,15 @@ def __init__(self): self._properties = [] def addProperty(self, property, alias): - self._properties.append( (property,alias) ) + self._properties.append((property, alias)) def properties(self): return self._properties def addDependency(self, headerfile, dir, module_dir): - deps = getDependencies(dir, headerfile) + getDependencies(module_dir, headerfile) + deps = getDependencies(dir, headerfile) + getDependencies( + module_dir, headerfile + ) for dep in deps: self._dependencies[dep] = 1 @@ -85,17 +90,23 @@ def addDependency(self, headerfile, dir, module_dir): def dependencies(self): return list(self._dependencies.keys()) -skip_metatypes = [ "QVariant", - "SireCAS::ExpressionBase", - "SireMaths::Rational", - "SireCluster::Node", - "SireCluster::Nodes", - "SireBase::PropertyPtr" ] + +skip_metatypes = [ + "QVariant", + "SireCAS::ExpressionBase", + "SireMaths::Rational", + "SireCluster::Node", + "SireCluster::Nodes", + "SireBase::PropertyPtr", +] + class HeaderInfo: def __init__(self, filename, dir, module_dir): self._filename = filename - self._dependencies = getDependencies(dir, filename) + getDependencies(module_dir, filename) + self._dependencies = getDependencies(dir, filename) + getDependencies( + module_dir, filename + ) self._classes = [] self._functions = [] self._aliases = {} @@ -109,7 +120,7 @@ def addFunction(self, func): self._functions.append(func) def addMetaType(self, classname): - #don't register some types + # don't register some types if classname in skip_metatypes: return @@ -119,7 +130,7 @@ def addAlias(self, classname, alias): self._aliases[classname] = alias def addProperty(self, prop, propbase): - self._properties.append( (prop, propbase) ) + self._properties.append((prop, propbase)) def dependencies(self): return self._dependencies @@ -142,6 +153,7 @@ def properties(self): def hasProperties(self): return len(self._properties) > 0 + match_class = r"SIREN*_EXPOSE_CLASS\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" match_alias = r"SIREN*_EXPOSE_ALIAS\(\s*\n*\s*\(?([<>,\-\s\w\d:]+)\)?\s*\n*,\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" match_function = r"SIREN*_EXPOSE_FUNCTION\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" @@ -156,6 +168,7 @@ def hasProperties(self): db = {} + def add_doc(function, docs, db): # now try to extract the function signature # - first, remove any derived bases from constructor lines @@ -177,9 +190,9 @@ def add_doc(function, docs, db): if arg.find("<") != -1: nopen = arg.count("<") - arg.count(">") - #template - see if there are multiple template arguments - while nopen > 0 and (i+1) < len(targs): - next_arg = targs[i+1].lstrip().rstrip() + # template - see if there are multiple template arguments + while nopen > 0 and (i + 1) < len(targs): + next_arg = targs[i + 1].lstrip().rstrip() arg += ", " + next_arg i += 1 nopen += next_arg.count("<") - next_arg.count(">") @@ -200,21 +213,22 @@ def add_doc(function, docs, db): if not nargs in db[cls][nam]: db[cls][nam][nargs] = [] - db[cls][nam][nargs].append( (args, docs) ) + db[cls][nam][nargs].append((args, docs)) + def extract_docs(filename, db): read_from = open(filename, "r") # concat to one line - file_str = '' + file_str = "" for line in read_from.readlines(): file_str += line # break off all code - file_str = re.sub('{', '\n{', file_str) + file_str = re.sub("{", "\n{", file_str) # remove '//' comments - file_str = re.sub('//.*', '', file_str) + file_str = re.sub("//.*", "", file_str) # split on '\n' file_as_list = file_str.splitlines(True) @@ -225,7 +239,7 @@ def extract_docs(filename, db): doc = "" function = "" - mode = 0 # 0 is nothing, 1 is comment, 2 is function + mode = 0 # 0 is nothing, 1 is comment, 2 is function # add in newlines where appropriate for line in file_as_list: @@ -233,31 +247,31 @@ def extract_docs(filename, db): added = False - if line.startswith('/*'): + if line.startswith("/*"): mode = 1 doc = line added = True - elif line.startswith('{'): - #print("code... (%s)" % mode) + elif line.startswith("{"): + # print("code... (%s)" % mode) if mode == 2: # hopefully we have seen a function - add_doc(function,doc,db) + add_doc(function, doc, db) doc = "" function = "" mode = 0 - if line.endswith('*/'): + if line.endswith("*/"): # end of comment - look for a function mode = 2 if not added: doc += "\n" + line added = True - #print("completed comment\n%s" % doc) + # print("completed comment\n%s" % doc) - elif line.endswith(';') or line.endswith('}'): - #print("line... (%s)" % mode) + elif line.endswith(";") or line.endswith("}"): + # print("line... (%s)" % mode) mode = 0 else: @@ -270,49 +284,57 @@ def extract_docs(filename, db): else: function += " " + line - #print("Function | %s" % function) + # print("Function | %s" % function) -def scanFiles(dir, module_dir, atom_properties, cg_properties, - res_properties, chain_properties, seg_properties, - bead_properties): + +def scanFiles( + dir, + module_dir, + atom_properties, + cg_properties, + res_properties, + chain_properties, + seg_properties, + bead_properties, +): """Scan the header files in the passed directory to get information - about all of the exposed classes, returning a list of all of - the classes that are being exposed, and placing meta information - into the directory 'module_dir'""" + about all of the exposed classes, returning a list of all of + the classes that are being exposed, and placing meta information + into the directory 'module_dir'""" h_files = getFiles(dir, "*.h") + getFiles(module_dir, "*.h") hpp_files = getFiles(dir, "*.hpp") + getFiles(module_dir, "*.hpp") cpp_files = getFiles(dir, "*.cpp") - #dictionary mapping files to exposed classes + # dictionary mapping files to exposed classes active_files = {} - #the list of exposed classes + # the list of exposed classes exposed_classes = [] - #the list of classes that have been registered with QMetaType + # the list of classes that have been registered with QMetaType meta_classes = [] - #database of all documentation + # database of all documentation doc_db = {} - #read through each .cpp file, looking for documentation + # read through each .cpp file, looking for documentation for file in cpp_files: try: - extract_docs("%s/%s" % (dir,file), doc_db) + extract_docs("%s/%s" % (dir, file), doc_db) except Exception as e: - print("Problem parsing %s | %s" % (file,e)) + print("Problem parsing %s | %s" % (file, e)) pass - #read each file, looking for SIRE_EXPOSE_FUNCTION or SIRE_EXPOSE_CLASS + # read each file, looking for SIRE_EXPOSE_FUNCTION or SIRE_EXPOSE_CLASS for file in h_files + hpp_files: if file.find("sirenglobal.h") != -1: continue try: - lines = open("%s/%s" % (dir,file), "r").readlines() + lines = open("%s/%s" % (dir, file), "r").readlines() except: - lines = open("%s/%s" % (module_dir,file), "r").readlines() + lines = open("%s/%s" % (module_dir, file), "r").readlines() text = " ".join(lines) @@ -338,7 +360,7 @@ def scanFiles(dir, module_dir, atom_properties, cg_properties, active_files[file].addFunction(m.groups()[0].strip()) for m in re.finditer(match_metatype, text): - #don't match the 'errors.h' files, as these are wrapped separately + # don't match the 'errors.h' files, as these are wrapped separately if file == "errors.h": continue @@ -431,55 +453,60 @@ def scanFiles(dir, module_dir, atom_properties, cg_properties, active_files[file].addAlias(classname, classname) exposed_classes.append(classname) - #now add each active file to a single header file that can be parsed by Py++ + # now add each active file to a single header file that can be parsed by Py++ FILE = open("%s/active_headers.h" % module_dir, "w") - print("#ifndef ACTIVE_HEADERS_H\n" + \ - "#define ACTIVE_HEADERS_H\n\n" + \ - "#ifdef GCCXML_PARSE\n", file=FILE) + print( + "#ifndef ACTIVE_HEADERS_H\n" + + "#define ACTIVE_HEADERS_H\n\n" + + "#ifdef GCCXML_PARSE\n", + file=FILE, + ) files = list(active_files.keys()) files.sort() for file in files: - print("#include \"%s\"" % file, file=FILE) + print('#include "%s"' % file, file=FILE) print("\n#endif\n\n#endif", file=FILE) FILE.close() - #now write out the active_files data structure so it can be - #used by other scripts - FILE = open("%s/active_headers.data" % module_dir,"wb") + # now write out the active_files data structure so it can be + # used by other scripts + FILE = open("%s/active_headers.data" % module_dir, "wb") pickle.dump(active_files, FILE) FILE.close() - #now write out the documentation data so it can be used by other scripts - FILE = open("%s/docs.data" % module_dir,"wb") + # now write out the documentation data so it can be used by other scripts + FILE = open("%s/docs.data" % module_dir, "wb") pickle.dump(doc_db, FILE) FILE.close() return exposed_classes -if __name__ == "__main__": - modules = { "Analysis" : "SireAnalysis", - "Base" : "SireBase", - "CAS" : "SireCAS", - "Cluster" : "SireCluster", - "FF" : "SireFF", - "ID" : "SireID", - "IO" : "SireIO", - "Maths" : "SireMaths", - "MM" : "SireMM", - "Mol" : "SireMol", - "Move" : "SireMove", - "Search" : "SireSearch", - "Squire" : "Squire", - "Stream" : "SireStream", - "System" : "SireSystem", - "Units" : "SireUnits", - "Vol" : "SireVol" } +if __name__ == "__main__": + modules = { + "Analysis": "SireAnalysis", + "Base": "SireBase", + "CAS": "SireCAS", + "Cluster": "SireCluster", + "FF": "SireFF", + "ID": "SireID", + "IO": "SireIO", + "Maths": "SireMaths", + "MM": "SireMM", + "Mol": "SireMol", + "Move": "SireMove", + "Search": "SireSearch", + "Squire": "Squire", + "Stream": "SireStream", + "System": "SireSystem", + "Units": "SireUnits", + "Vol": "SireVol", + } if len(sys.argv) < 3: print("USAGE: python scanheaders.py input_path output_path") @@ -498,13 +525,12 @@ def scanFiles(dir, module_dir, atom_properties, cg_properties, bead_properties = Properties() for module in modules: - try: - os.makedirs( "%s/%s" % (outdir,module) ) + os.makedirs("%s/%s" % (outdir, module)) except: pass - FILE = open("%s/%s/module_info" % (outdir,module), "w") + FILE = open("%s/%s/module_info" % (outdir, module), "w") print("Module %s" % module, file=FILE) print("Source %s" % modules[module], file=FILE) @@ -512,22 +538,27 @@ def scanFiles(dir, module_dir, atom_properties, cg_properties, FILE.close() - module_classes = scanFiles( "%s/%s" % (siredir,modules[module]), - "%s/%s" % (outdir,module), - atom_properties, cg_properties, res_properties, - chain_properties, seg_properties, bead_properties ) + module_classes = scanFiles( + "%s/%s" % (siredir, modules[module]), + "%s/%s" % (outdir, module), + atom_properties, + cg_properties, + res_properties, + chain_properties, + seg_properties, + bead_properties, + ) for clas in module_classes: exposed_classes[clas] = 1 + # write the set of exposed classes to a data file to be used + # by other scripts + pickle.dump(exposed_classes, open("%s/classdb.data" % outdir, "wb")) - #write the set of exposed classes to a data file to be used - #by other scripts - pickle.dump( exposed_classes, open("%s/classdb.data" % outdir, "wb") ) - - pickle.dump( atom_properties, open("%s/Mol/atomprops.data" % outdir, "wb") ) - pickle.dump( cg_properties, open("%s/Mol/cgprops.data" % outdir, "wb") ) - pickle.dump( res_properties, open("%s/Mol/resprops.data" % outdir, "wb") ) - pickle.dump( chain_properties, open("%s/Mol/chainprops.data" % outdir, "wb") ) - pickle.dump( seg_properties, open("%s/Mol/segprops.data" % outdir, "wb") ) - pickle.dump( bead_properties, open("%s/Mol/beadprops.data" % outdir, "wb") ) + pickle.dump(atom_properties, open("%s/Mol/atomprops.data" % outdir, "wb")) + pickle.dump(cg_properties, open("%s/Mol/cgprops.data" % outdir, "wb")) + pickle.dump(res_properties, open("%s/Mol/resprops.data" % outdir, "wb")) + pickle.dump(chain_properties, open("%s/Mol/chainprops.data" % outdir, "wb")) + pickle.dump(seg_properties, open("%s/Mol/segprops.data" % outdir, "wb")) + pickle.dump(bead_properties, open("%s/Mol/beadprops.data" % outdir, "wb")) diff --git a/wrapper/CAS/LambdaSchedule.pypp.cpp b/wrapper/CAS/LambdaSchedule.pypp.cpp index 6fd39eb88..f2709cd9e 100644 --- a/wrapper/CAS/LambdaSchedule.pypp.cpp +++ b/wrapper/CAS/LambdaSchedule.pypp.cpp @@ -3,6 +3,7 @@ // (C) Christopher Woods, GPL >= 3 License #include "boost/python.hpp" +#include "Helpers/clone_const_reference.hpp" #include "LambdaSchedule.pypp.hpp" namespace bp = boost::python; @@ -55,6 +56,56 @@ void register_LambdaSchedule_class(){ "addChargeScaleStages" , addChargeScaleStages_function_value , ( bp::arg("decharge_name"), bp::arg("recharge_name"), bp::arg("scale")=0.20000000000000001 ) + , "Sandwich the current set of stages with a charge-descaling and\n a charge-scaling stage. This prepends a charge-descaling stage\n that scales the charge parameter down from `initial` to\n :gamma:.initial (where :gamma:=`scale`). The charge parameter in all of\n the exising stages in this schedule are then multiplied\n by :gamma:. A final charge-rescaling stage is then appended that\n scales the charge parameter from :gamma:.final to final.\n" ); + + } + { //::SireCAS::LambdaSchedule::addDecoupleStage + + typedef void ( ::SireCAS::LambdaSchedule::*addDecoupleStage_function_type)( bool ) ; + addDecoupleStage_function_type addDecoupleStage_function_value( &::SireCAS::LambdaSchedule::addDecoupleStage ); + + LambdaSchedule_exposer.def( + "addDecoupleStage" + , addDecoupleStage_function_value + , ( bp::arg("perturbed_is_decoupled")=(bool)(true) ) + , "" ); + + } + { //::SireCAS::LambdaSchedule::addDecoupleStage + + typedef void ( ::SireCAS::LambdaSchedule::*addDecoupleStage_function_type)( ::QString const &,bool ) ; + addDecoupleStage_function_type addDecoupleStage_function_value( &::SireCAS::LambdaSchedule::addDecoupleStage ); + + LambdaSchedule_exposer.def( + "addDecoupleStage" + , addDecoupleStage_function_value + , ( bp::arg("name"), bp::arg("perturbed_is_decoupled")=(bool)(true) ) + , "" ); + + } + { //::SireCAS::LambdaSchedule::addForce + + typedef void ( ::SireCAS::LambdaSchedule::*addForce_function_type)( ::QString const & ) ; + addForce_function_type addForce_function_value( &::SireCAS::LambdaSchedule::addForce ); + + LambdaSchedule_exposer.def( + "addForce" + , addForce_function_value + , ( bp::arg("force") ) + , bp::release_gil_policy() + , "" ); + + } + { //::SireCAS::LambdaSchedule::addForces + + typedef void ( ::SireCAS::LambdaSchedule::*addForces_function_type)( ::QStringList const & ) ; + addForces_function_type addForces_function_value( &::SireCAS::LambdaSchedule::addForces ); + + LambdaSchedule_exposer.def( + "addForces" + , addForces_function_value + , ( bp::arg("forces") ) + , bp::release_gil_policy() , "" ); } @@ -106,7 +157,7 @@ void register_LambdaSchedule_class(){ , addMorphStage_function_value , ( bp::arg("name") ) , bp::release_gil_policy() - , "" ); + , "Append a morph stage onto this schedule. The morph stage is a\n standard stage that scales each forcefield parameter by\n (1-:lambda:).initial + :lambda:.final\n" ); } { //::SireCAS::LambdaSchedule::addStage @@ -134,6 +185,18 @@ void register_LambdaSchedule_class(){ , bp::release_gil_policy() , "Append a stage called name which uses the passed equation\n to the end of this schedule. The equation will be the default\n equation that scales all parameters (levers) that dont have\n a custom lever for this stage.\n" ); + } + { //::SireCAS::LambdaSchedule::charge_scaled_decouple + + typedef ::SireCAS::LambdaSchedule ( *charge_scaled_decouple_function_type )( double,bool ); + charge_scaled_decouple_function_type charge_scaled_decouple_function_value( &::SireCAS::LambdaSchedule::charge_scaled_decouple ); + + LambdaSchedule_exposer.def( + "charge_scaled_decouple" + , charge_scaled_decouple_function_value + , ( bp::arg("scale")=0.20000000000000001, bp::arg("perturbed_is_decoupled")=(bool)(true) ) + , "" ); + } { //::SireCAS::LambdaSchedule::charge_scaled_morph @@ -225,28 +288,26 @@ void register_LambdaSchedule_class(){ } { //::SireCAS::LambdaSchedule::getEquation - typedef ::SireCAS::Expression ( ::SireCAS::LambdaSchedule::*getEquation_function_type)( ::QString const & ) const; + typedef ::SireCAS::Expression ( ::SireCAS::LambdaSchedule::*getEquation_function_type)( ::QString const &,::QString const &,::QString const & ) const; getEquation_function_type getEquation_function_value( &::SireCAS::LambdaSchedule::getEquation ); LambdaSchedule_exposer.def( "getEquation" , getEquation_function_value - , ( bp::arg("stage") ) - , bp::release_gil_policy() - , "Return the default equation used to control the parameters for\n the stage `stage`.\n" ); + , ( bp::arg("stage")="*", bp::arg("force")="*", bp::arg("lever")="*" ) + , "" ); } - { //::SireCAS::LambdaSchedule::getEquation + { //::SireCAS::LambdaSchedule::getForces - typedef ::SireCAS::Expression ( ::SireCAS::LambdaSchedule::*getEquation_function_type)( ::QString const &,::QString const & ) const; - getEquation_function_type getEquation_function_value( &::SireCAS::LambdaSchedule::getEquation ); + typedef ::QStringList ( ::SireCAS::LambdaSchedule::*getForces_function_type)( ) const; + getForces_function_type getForces_function_value( &::SireCAS::LambdaSchedule::getForces ); LambdaSchedule_exposer.def( - "getEquation" - , getEquation_function_value - , ( bp::arg("stage"), bp::arg("lever") ) + "getForces" + , getForces_function_value , bp::release_gil_policy() - , "Return the equation used to control the specified `lever`\n at the specified `stage`. This will be a custom equation\n if that has been set for this lever, or else the\n default equation for this stage.\n" ); + , "" ); } { //::SireCAS::LambdaSchedule::getLambdaInStage @@ -322,6 +383,19 @@ void register_LambdaSchedule_class(){ , bp::release_gil_policy() , "Return all of the levers that have been explicitly added\n to the schedule. Note that levers will be automatically added\n by any perturbation run that needs them, so you dont normally\n need to manage them manually yourself.\n" ); + } + { //::SireCAS::LambdaSchedule::getMoleculeSchedule + + typedef ::SireCAS::LambdaSchedule const & ( ::SireCAS::LambdaSchedule::*getMoleculeSchedule_function_type)( int ) const; + getMoleculeSchedule_function_type getMoleculeSchedule_function_value( &::SireCAS::LambdaSchedule::getMoleculeSchedule ); + + LambdaSchedule_exposer.def( + "getMoleculeSchedule" + , getMoleculeSchedule_function_value + , ( bp::arg("pert_mol_id") ) + , bp::return_value_policy() + , "" ); + } { //::SireCAS::LambdaSchedule::getStage @@ -347,6 +421,31 @@ void register_LambdaSchedule_class(){ , bp::release_gil_policy() , "Return the names of all of the stages in this schedule, in\n the order they will be performed\n" ); + } + { //::SireCAS::LambdaSchedule::hasForceSpecificEquation + + typedef bool ( ::SireCAS::LambdaSchedule::*hasForceSpecificEquation_function_type)( ::QString const &,::QString const &,::QString const & ) const; + hasForceSpecificEquation_function_type hasForceSpecificEquation_function_value( &::SireCAS::LambdaSchedule::hasForceSpecificEquation ); + + LambdaSchedule_exposer.def( + "hasForceSpecificEquation" + , hasForceSpecificEquation_function_value + , ( bp::arg("stage")="*", bp::arg("force")="*", bp::arg("lever")="*" ) + , "" ); + + } + { //::SireCAS::LambdaSchedule::hasMoleculeSchedule + + typedef bool ( ::SireCAS::LambdaSchedule::*hasMoleculeSchedule_function_type)( int ) const; + hasMoleculeSchedule_function_type hasMoleculeSchedule_function_value( &::SireCAS::LambdaSchedule::hasMoleculeSchedule ); + + LambdaSchedule_exposer.def( + "hasMoleculeSchedule" + , hasMoleculeSchedule_function_value + , ( bp::arg("pert_mol_id") ) + , bp::release_gil_policy() + , "" ); + } { //::SireCAS::LambdaSchedule::initial @@ -399,41 +498,50 @@ void register_LambdaSchedule_class(){ } { //::SireCAS::LambdaSchedule::morph - typedef double ( ::SireCAS::LambdaSchedule::*morph_function_type)( ::QString const &,double,double,double ) const; + typedef double ( ::SireCAS::LambdaSchedule::*morph_function_type)( ::QString const &,::QString const &,double,double,double ) const; morph_function_type morph_function_value( &::SireCAS::LambdaSchedule::morph ); LambdaSchedule_exposer.def( "morph" , morph_function_value - , ( bp::arg("lever"), bp::arg("initial"), bp::arg("final"), bp::arg("lambda_value") ) - , bp::release_gil_policy() - , "Return the parameters for the specified lever called `lever_name`\n that have been morphed from the passed list of initial values\n (in `initial`) to the passed list of final values (in `final`)\n for the specified global value of :lambda: (in `lambda_value`).\n\n The morphed parameters will be returned in the matching\n order to `initial` and `final`.\n\n This morphs a single floating point parameters.\n" ); + , ( bp::arg("force")="*", bp::arg("lever")="*", bp::arg("initial")=0, bp::arg("final")=1, bp::arg("lambda_value")=0 ) + , "" ); } { //::SireCAS::LambdaSchedule::morph - typedef ::QVector< double > ( ::SireCAS::LambdaSchedule::*morph_function_type)( ::QString const &,::QVector< double > const &,::QVector< double > const &,double ) const; + typedef ::QVector< double > ( ::SireCAS::LambdaSchedule::*morph_function_type)( ::QString const &,::QString const &,::QVector< double > const &,::QVector< double > const &,double ) const; morph_function_type morph_function_value( &::SireCAS::LambdaSchedule::morph ); LambdaSchedule_exposer.def( "morph" , morph_function_value - , ( bp::arg("lever"), bp::arg("initial"), bp::arg("final"), bp::arg("lambda_value") ) - , bp::release_gil_policy() - , "Return the parameters for the specified lever called `lever_name`\n that have been morphed from the passed list of initial values\n (in `initial`) to the passed list of final values (in `final`)\n for the specified global value of :lambda: (in `lambda_value`).\n\n The morphed parameters will be returned in the matching\n order to `initial` and `final`.\n\n This morphs floating point parameters. There is an overload\n of this function that morphs integer parameters, in which\n case the result would be rounded to the nearest integer.\n" ); + , ( bp::arg("force")="*", bp::arg("lever")="*", bp::arg("initial")=::QVector( ), bp::arg("final")=::QVector( ), bp::arg("lambda_value")=0. ) + , "" ); } { //::SireCAS::LambdaSchedule::morph - typedef ::QVector< int > ( ::SireCAS::LambdaSchedule::*morph_function_type)( ::QString const &,::QVector< int > const &,::QVector< int > const &,double ) const; + typedef ::QVector< int > ( ::SireCAS::LambdaSchedule::*morph_function_type)( ::QString const &,::QString const &,::QVector< int > const &,::QVector< int > const &,double ) const; morph_function_type morph_function_value( &::SireCAS::LambdaSchedule::morph ); LambdaSchedule_exposer.def( "morph" , morph_function_value - , ( bp::arg("lever"), bp::arg("initial"), bp::arg("final"), bp::arg("lambda_value") ) + , ( bp::arg("force")="*", bp::arg("lever")="*", bp::arg("initial")=::QVector( ), bp::arg("final")=::QVector( ), bp::arg("lambda_value")=0. ) + , "" ); + + } + { //::SireCAS::LambdaSchedule::nForces + + typedef int ( ::SireCAS::LambdaSchedule::*nForces_function_type)( ) const; + nForces_function_type nForces_function_value( &::SireCAS::LambdaSchedule::nForces ); + + LambdaSchedule_exposer.def( + "nForces" + , nForces_function_value , bp::release_gil_policy() - , "Return the parameters for the specified lever called `lever_name`\n that have been morphed from the passed list of initial values\n (in `initial`) to the passed list of final values (in `final`)\n for the specified global value of :lambda: (in `lambda_value`).\n\n The morphed parameters will be returned in the matching\n order to `initial` and `final`.\n\n This function morphs integer parameters. In this case,\n the result will be the rounded to the nearest integer.\n" ); + , "" ); } { //::SireCAS::LambdaSchedule::nLevers @@ -490,15 +598,40 @@ void register_LambdaSchedule_class(){ } { //::SireCAS::LambdaSchedule::removeEquation - typedef void ( ::SireCAS::LambdaSchedule::*removeEquation_function_type)( ::QString const &,::QString const & ) ; + typedef void ( ::SireCAS::LambdaSchedule::*removeEquation_function_type)( ::QString const &,::QString const &,::QString const & ) ; removeEquation_function_type removeEquation_function_value( &::SireCAS::LambdaSchedule::removeEquation ); LambdaSchedule_exposer.def( "removeEquation" , removeEquation_function_value - , ( bp::arg("stage"), bp::arg("lever") ) + , ( bp::arg("stage")="*", bp::arg("force")="*", bp::arg("lever")="*" ) + , "" ); + + } + { //::SireCAS::LambdaSchedule::removeForce + + typedef void ( ::SireCAS::LambdaSchedule::*removeForce_function_type)( ::QString const & ) ; + removeForce_function_type removeForce_function_value( &::SireCAS::LambdaSchedule::removeForce ); + + LambdaSchedule_exposer.def( + "removeForce" + , removeForce_function_value + , ( bp::arg("force") ) , bp::release_gil_policy() - , "Remove the custom equation for the specified `lever` at the\n specified `stage`. The lever will now use the default\n equation at this stage.\n" ); + , "" ); + + } + { //::SireCAS::LambdaSchedule::removeForces + + typedef void ( ::SireCAS::LambdaSchedule::*removeForces_function_type)( ::QStringList const & ) ; + removeForces_function_type removeForces_function_value( &::SireCAS::LambdaSchedule::removeForces ); + + LambdaSchedule_exposer.def( + "removeForces" + , removeForces_function_value + , ( bp::arg("forces") ) + , bp::release_gil_policy() + , "" ); } { //::SireCAS::LambdaSchedule::removeLever @@ -526,6 +659,32 @@ void register_LambdaSchedule_class(){ , bp::release_gil_policy() , "Remove some levers from the schedule. This will not impact any\n perturbation runs that use this schedule, as any missing\n levers will be re-added.\n" ); + } + { //::SireCAS::LambdaSchedule::removeMoleculeSchedule + + typedef void ( ::SireCAS::LambdaSchedule::*removeMoleculeSchedule_function_type)( int ) ; + removeMoleculeSchedule_function_type removeMoleculeSchedule_function_value( &::SireCAS::LambdaSchedule::removeMoleculeSchedule ); + + LambdaSchedule_exposer.def( + "removeMoleculeSchedule" + , removeMoleculeSchedule_function_value + , ( bp::arg("pert_mol_id") ) + , bp::release_gil_policy() + , "" ); + + } + { //::SireCAS::LambdaSchedule::removeStage + + typedef void ( ::SireCAS::LambdaSchedule::*removeStage_function_type)( ::QString const & ) ; + removeStage_function_type removeStage_function_value( &::SireCAS::LambdaSchedule::removeStage ); + + LambdaSchedule_exposer.def( + "removeStage" + , removeStage_function_value + , ( bp::arg("stage") ) + , bp::release_gil_policy() + , "" ); + } { //::SireCAS::LambdaSchedule::setConstant @@ -553,30 +712,54 @@ void register_LambdaSchedule_class(){ , "Set the value of a constant that may be used in any\n of the stage equations.\n" ); } - { //::SireCAS::LambdaSchedule::setDefaultEquation + { //::SireCAS::LambdaSchedule::setDefaultStageEquation - typedef void ( ::SireCAS::LambdaSchedule::*setDefaultEquation_function_type)( ::QString const &,::SireCAS::Expression const & ) ; - setDefaultEquation_function_type setDefaultEquation_function_value( &::SireCAS::LambdaSchedule::setDefaultEquation ); + typedef void ( ::SireCAS::LambdaSchedule::*setDefaultStageEquation_function_type)( ::QString const &,::SireCAS::Expression const & ) ; + setDefaultStageEquation_function_type setDefaultStageEquation_function_value( &::SireCAS::LambdaSchedule::setDefaultStageEquation ); LambdaSchedule_exposer.def( - "setDefaultEquation" - , setDefaultEquation_function_value + "setDefaultStageEquation" + , setDefaultStageEquation_function_value , ( bp::arg("stage"), bp::arg("equation") ) , bp::release_gil_policy() - , "Set the default equation used to control levers for the\n stage stage to equation. This equation will be used\n to control any levers in this stage that dont have\n their own custom equation.\n" ); + , "" ); } { //::SireCAS::LambdaSchedule::setEquation - typedef void ( ::SireCAS::LambdaSchedule::*setEquation_function_type)( ::QString const &,::QString const &,::SireCAS::Expression const & ) ; + typedef void ( ::SireCAS::LambdaSchedule::*setEquation_function_type)( ::QString const &,::QString const &,::QString const &,::SireCAS::Expression const & ) ; setEquation_function_type setEquation_function_value( &::SireCAS::LambdaSchedule::setEquation ); LambdaSchedule_exposer.def( "setEquation" , setEquation_function_value - , ( bp::arg("stage"), bp::arg("lever"), bp::arg("equation") ) + , ( bp::arg("stage")="*", bp::arg("force")="*", bp::arg("lever")="*", bp::arg("equation")=SireCAS::Expression() ) + , "" ); + + } + { //::SireCAS::LambdaSchedule::setMoleculeSchedule + + typedef void ( ::SireCAS::LambdaSchedule::*setMoleculeSchedule_function_type)( int,::SireCAS::LambdaSchedule const & ) ; + setMoleculeSchedule_function_type setMoleculeSchedule_function_value( &::SireCAS::LambdaSchedule::setMoleculeSchedule ); + + LambdaSchedule_exposer.def( + "setMoleculeSchedule" + , setMoleculeSchedule_function_value + , ( bp::arg("pert_mol_id"), bp::arg("schedule") ) , bp::release_gil_policy() - , "Set the custom equation used to control the specified\n `lever` at the stage `stage` to `equation`. This equation\n will only be used to control the parameters for the\n specified lever at the specified stage.\n" ); + , "" ); + + } + { //::SireCAS::LambdaSchedule::standard_decouple + + typedef ::SireCAS::LambdaSchedule ( *standard_decouple_function_type )( bool ); + standard_decouple_function_type standard_decouple_function_value( &::SireCAS::LambdaSchedule::standard_decouple ); + + LambdaSchedule_exposer.def( + "standard_decouple" + , standard_decouple_function_value + , ( bp::arg("perturbed_is_decoupled")=(bool)(true) ) + , "" ); } { //::SireCAS::LambdaSchedule::standard_morph @@ -590,6 +773,19 @@ void register_LambdaSchedule_class(){ , bp::release_gil_policy() , "Return a LambdaSchedule that represents a standard morph,\n where every forcefield parameter is scaled by\n (1-:lambda:).initial + :lambda:.final\n" ); + } + { //::SireCAS::LambdaSchedule::takeMoleculeSchedule + + typedef ::SireCAS::LambdaSchedule ( ::SireCAS::LambdaSchedule::*takeMoleculeSchedule_function_type)( int ) ; + takeMoleculeSchedule_function_type takeMoleculeSchedule_function_value( &::SireCAS::LambdaSchedule::takeMoleculeSchedule ); + + LambdaSchedule_exposer.def( + "takeMoleculeSchedule" + , takeMoleculeSchedule_function_value + , ( bp::arg("pert_mol_id") ) + , bp::release_gil_policy() + , "" ); + } { //::SireCAS::LambdaSchedule::toString @@ -627,10 +823,12 @@ void register_LambdaSchedule_class(){ , "" ); } + LambdaSchedule_exposer.staticmethod( "charge_scaled_decouple" ); LambdaSchedule_exposer.staticmethod( "charge_scaled_morph" ); LambdaSchedule_exposer.staticmethod( "final" ); LambdaSchedule_exposer.staticmethod( "initial" ); LambdaSchedule_exposer.staticmethod( "lam" ); + LambdaSchedule_exposer.staticmethod( "standard_decouple" ); LambdaSchedule_exposer.staticmethod( "standard_morph" ); LambdaSchedule_exposer.staticmethod( "typeName" ); LambdaSchedule_exposer.def( "__copy__", &__copy__); diff --git a/wrapper/CAS/SireCAS_registrars.cpp b/wrapper/CAS/SireCAS_registrars.cpp index a974d3fa3..57eb6df46 100644 --- a/wrapper/CAS/SireCAS_registrars.cpp +++ b/wrapper/CAS/SireCAS_registrars.cpp @@ -3,94 +3,94 @@ #include "SireCAS_registrars.h" -#include "abs.h" -#include "complexvalues.h" -#include "conditional.h" -#include "constant.h" -#include "exp.h" -#include "expression.h" -#include "expressionproperty.h" -#include "function.h" -#include "functionsignature.h" -#include "hyperbolicfuncs.h" -#include "i.h" -#include "identities.h" -#include "integrationconstant.h" -#include "invhyperbolicfuncs.h" -#include "invtrigfuncs.h" -#include "lambdaschedule.h" -#include "minmax.h" #include "power.h" +#include "minmax.h" #include "powerconstant.h" #include "product.h" -#include "sum.h" #include "symbol.h" -#include "trigfuncs.h" +#include "invtrigfuncs.h" +#include "invhyperbolicfuncs.h" +#include "constant.h" +#include "abs.h" +#include "hyperbolicfuncs.h" +#include "i.h" +#include "sum.h" #include "values.h" +#include "expression.h" +#include "functionsignature.h" +#include "complexvalues.h" +#include "integrationconstant.h" +#include "expressionproperty.h" +#include "exp.h" +#include "identities.h" +#include "trigfuncs.h" +#include "lambdaschedule.h" +#include "function.h" +#include "conditional.h" #include "Helpers/objectregistry.hpp" void register_SireCAS_objects() { - ObjectRegistry::registerConverterFor< SireCAS::Abs >(); - ObjectRegistry::registerConverterFor< SireCAS::ComplexValues >(); - ObjectRegistry::registerConverterFor< SireCAS::Conditional >(); - ObjectRegistry::registerConverterFor< SireCAS::GreaterThan >(); - ObjectRegistry::registerConverterFor< SireCAS::LessThan >(); - ObjectRegistry::registerConverterFor< SireCAS::GreaterOrEqualThan >(); - ObjectRegistry::registerConverterFor< SireCAS::LessOrEqualThan >(); - ObjectRegistry::registerConverterFor< SireCAS::EqualTo >(); - ObjectRegistry::registerConverterFor< SireCAS::NotEqualTo >(); - ObjectRegistry::registerConverterFor< SireCAS::AlwaysTrue >(); - ObjectRegistry::registerConverterFor< SireCAS::AlwaysFalse >(); - ObjectRegistry::registerConverterFor< SireCAS::Constant >(); - ObjectRegistry::registerConverterFor< SireCAS::Exp >(); - ObjectRegistry::registerConverterFor< SireCAS::Ln >(); - ObjectRegistry::registerConverterFor< SireCAS::Expression >(); - ObjectRegistry::registerConverterFor< SireCAS::ExpressionProperty >(); - ObjectRegistry::registerConverterFor< SireCAS::Function >(); - ObjectRegistry::registerConverterFor< SireCAS::FunctionSignature >(); - ObjectRegistry::registerConverterFor< SireCAS::Cosh >(); - ObjectRegistry::registerConverterFor< SireCAS::Sinh >(); - ObjectRegistry::registerConverterFor< SireCAS::Tanh >(); - ObjectRegistry::registerConverterFor< SireCAS::Csch >(); - ObjectRegistry::registerConverterFor< SireCAS::Sech >(); - ObjectRegistry::registerConverterFor< SireCAS::Coth >(); - ObjectRegistry::registerConverterFor< SireCAS::I >(); - ObjectRegistry::registerConverterFor< SireCAS::Identities >(); - ObjectRegistry::registerConverterFor< SireCAS::IntegrationConstant >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcCosh >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcSinh >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcTanh >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcCsch >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcSech >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcCoth >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcCos >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcSin >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcTan >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcCsc >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcSec >(); - ObjectRegistry::registerConverterFor< SireCAS::ArcCot >(); - ObjectRegistry::registerConverterFor< SireCAS::LambdaSchedule >(); + ObjectRegistry::registerConverterFor< SireCAS::Power >(); ObjectRegistry::registerConverterFor< SireCAS::Min >(); ObjectRegistry::registerConverterFor< SireCAS::Max >(); - ObjectRegistry::registerConverterFor< SireCAS::Power >(); ObjectRegistry::registerConverterFor< SireCAS::PowerConstant >(); ObjectRegistry::registerConverterFor< SireCAS::IntegerPower >(); ObjectRegistry::registerConverterFor< SireCAS::RationalPower >(); ObjectRegistry::registerConverterFor< SireCAS::RealPower >(); ObjectRegistry::registerConverterFor< SireCAS::ComplexPower >(); ObjectRegistry::registerConverterFor< SireCAS::Product >(); - ObjectRegistry::registerConverterFor< SireCAS::Sum >(); ObjectRegistry::registerConverterFor< SireCAS::Symbol >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcCos >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcSin >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcTan >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcCsc >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcSec >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcCot >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcCosh >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcSinh >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcTanh >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcCsch >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcSech >(); + ObjectRegistry::registerConverterFor< SireCAS::ArcCoth >(); + ObjectRegistry::registerConverterFor< SireCAS::Constant >(); + ObjectRegistry::registerConverterFor< SireCAS::Abs >(); + ObjectRegistry::registerConverterFor< SireCAS::Cosh >(); + ObjectRegistry::registerConverterFor< SireCAS::Sinh >(); + ObjectRegistry::registerConverterFor< SireCAS::Tanh >(); + ObjectRegistry::registerConverterFor< SireCAS::Csch >(); + ObjectRegistry::registerConverterFor< SireCAS::Sech >(); + ObjectRegistry::registerConverterFor< SireCAS::Coth >(); + ObjectRegistry::registerConverterFor< SireCAS::I >(); + ObjectRegistry::registerConverterFor< SireCAS::Sum >(); + ObjectRegistry::registerConverterFor< SireCAS::Values >(); + ObjectRegistry::registerConverterFor< SireCAS::Expression >(); + ObjectRegistry::registerConverterFor< SireCAS::FunctionSignature >(); + ObjectRegistry::registerConverterFor< SireCAS::ComplexValues >(); + ObjectRegistry::registerConverterFor< SireCAS::IntegrationConstant >(); + ObjectRegistry::registerConverterFor< SireCAS::ExpressionProperty >(); + ObjectRegistry::registerConverterFor< SireCAS::Exp >(); + ObjectRegistry::registerConverterFor< SireCAS::Ln >(); + ObjectRegistry::registerConverterFor< SireCAS::Identities >(); ObjectRegistry::registerConverterFor< SireCAS::Cos >(); ObjectRegistry::registerConverterFor< SireCAS::Sin >(); ObjectRegistry::registerConverterFor< SireCAS::Tan >(); ObjectRegistry::registerConverterFor< SireCAS::Csc >(); ObjectRegistry::registerConverterFor< SireCAS::Sec >(); ObjectRegistry::registerConverterFor< SireCAS::Cot >(); - ObjectRegistry::registerConverterFor< SireCAS::Values >(); + ObjectRegistry::registerConverterFor< SireCAS::LambdaSchedule >(); + ObjectRegistry::registerConverterFor< SireCAS::Function >(); + ObjectRegistry::registerConverterFor< SireCAS::Conditional >(); + ObjectRegistry::registerConverterFor< SireCAS::GreaterThan >(); + ObjectRegistry::registerConverterFor< SireCAS::LessThan >(); + ObjectRegistry::registerConverterFor< SireCAS::GreaterOrEqualThan >(); + ObjectRegistry::registerConverterFor< SireCAS::LessOrEqualThan >(); + ObjectRegistry::registerConverterFor< SireCAS::EqualTo >(); + ObjectRegistry::registerConverterFor< SireCAS::NotEqualTo >(); + ObjectRegistry::registerConverterFor< SireCAS::AlwaysTrue >(); + ObjectRegistry::registerConverterFor< SireCAS::AlwaysFalse >(); } diff --git a/wrapper/CAS/_CAS_free_functions.pypp.cpp b/wrapper/CAS/_CAS_free_functions.pypp.cpp index f60bd9b93..590789200 100644 --- a/wrapper/CAS/_CAS_free_functions.pypp.cpp +++ b/wrapper/CAS/_CAS_free_functions.pypp.cpp @@ -83,26 +83,6 @@ namespace bp = boost::python; #include "expression.h" -#include "SireCAS/errors.h" - -#include "SireStream/datastream.h" - -#include "complexvalues.h" - -#include "exbase.h" - -#include "expression.h" - -#include "expressionbase.h" - -#include "functions.h" - -#include "identities.h" - -#include "values.h" - -#include "exbase.h" - #include "SireMaths/complex.h" #include "SireMaths/maths.h" @@ -779,6 +759,26 @@ namespace bp = boost::python; #include "expression.h" +#include "SireCAS/errors.h" + +#include "SireStream/datastream.h" + +#include "complexvalues.h" + +#include "exbase.h" + +#include "expression.h" + +#include "expressionbase.h" + +#include "functions.h" + +#include "identities.h" + +#include "values.h" + +#include "exbase.h" + #include "SireMaths/complex.h" #include "SireMaths/maths.h" diff --git a/wrapper/Convert/SireOpenMM/CMakeAutogenFile.txt b/wrapper/Convert/SireOpenMM/CMakeAutogenFile.txt new file mode 100644 index 000000000..1a668ae58 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/CMakeAutogenFile.txt @@ -0,0 +1,9 @@ +# WARNING - AUTOGENERATED FILE - CONTENTS WILL BE OVERWRITTEN! +set ( PYPP_SOURCES + _SireOpenMM_free_functions.pypp.cpp + LambdaLever.pypp.cpp + vector_less__OpenMM_scope_Vec3__greater_.pypp.cpp + PerturbableOpenMMMolecule.pypp.cpp + OpenMMMetaData.pypp.cpp + SireOpenMM_registrars.cpp + ) diff --git a/wrapper/Convert/SireOpenMM/CMakeLists.txt b/wrapper/Convert/SireOpenMM/CMakeLists.txt index ffde94ed2..9157141cb 100644 --- a/wrapper/Convert/SireOpenMM/CMakeLists.txt +++ b/wrapper/Convert/SireOpenMM/CMakeLists.txt @@ -43,19 +43,24 @@ if (${SIRE_USE_OPENMM}) message( STATUS "The free energy code will be a little slower.") endif() + # Get the list of autogenerated files + include(CMakeAutogenFile.txt) + # Define the sources in SireOpenMM set ( SIREOPENMM_SOURCES - _SireOpenMM.main.cpp - lambdalever.cpp openmmminimise.cpp openmmmolecule.cpp + register_extras.cpp sire_openmm.cpp sire_to_openmm_system.cpp lbgfs/lbfgs.cpp + _SireOpenMM.main.cpp + + ${PYPP_SOURCES} ) # Create the library that holds all of the class wrappers @@ -94,7 +99,7 @@ if (${SIRE_USE_OPENMM}) RUNTIME DESTINATION ${INSTALLDIR} ) - install( FILES _sommcontext.py + install( FILES _sommcontext.py _perturbablemol.py DESTINATION ${INSTALLDIR} ) else() diff --git a/wrapper/Convert/SireOpenMM/LambdaLever.pypp.cpp b/wrapper/Convert/SireOpenMM/LambdaLever.pypp.cpp new file mode 100644 index 000000000..b6041701a --- /dev/null +++ b/wrapper/Convert/SireOpenMM/LambdaLever.pypp.cpp @@ -0,0 +1,279 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#include "boost/python.hpp" +#include "LambdaLever.pypp.hpp" + +namespace bp = boost::python; + +#include "SireCAS/values.h" + +#include "lambdalever.h" + +#include "tostring.h" + +#include "SireCAS/values.h" + +#include "lambdalever.h" + +#include "tostring.h" + +SireOpenMM::LambdaLever __copy__(const SireOpenMM::LambdaLever &other){ return SireOpenMM::LambdaLever(other); } + +#include "Helpers/str.hpp" + +#include "Helpers/release_gil_policy.hpp" + +#include "SireCAS/values.h" + +#include "lambdalever.h" + +#include "tostring.h" + +#include "SireCAS/values.h" + +#include "lambdalever.h" + +#include "tostring.h" + +#include "Helpers/str.hpp" + +#include "Helpers/release_gil_policy.hpp" + +void register_LambdaLever_class(){ + + { //::SireOpenMM::LambdaLever + typedef bp::class_< SireOpenMM::LambdaLever > LambdaLever_exposer_t; + LambdaLever_exposer_t LambdaLever_exposer = LambdaLever_exposer_t( "LambdaLever", "This is a lever that is used to change the parameters in an OpenMM\ncontext according to a lambda value. This is actually a collection\nof levers, each of which is controlled by the main lever.\n\nYou can use SireCAS expressions to control how each lever changes\neach parameter\n", bp::init< >("") ); + bp::scope LambdaLever_scope( LambdaLever_exposer ); + LambdaLever_exposer.def( bp::init< SireOpenMM::LambdaLever const & >(( bp::arg("other") ), "") ); + { //::SireOpenMM::LambdaLever::addLever + + typedef void ( ::SireOpenMM::LambdaLever::*addLever_function_type)( ::QString const & ) ; + addLever_function_type addLever_function_value( &::SireOpenMM::LambdaLever::addLever ); + + LambdaLever_exposer.def( + "addLever" + , addLever_function_value + , ( bp::arg("lever_name") ) + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::LambdaLever::addPerturbableMolecule + + typedef int ( ::SireOpenMM::LambdaLever::*addPerturbableMolecule_function_type)( ::SireOpenMM::OpenMMMolecule const &,::QHash< QString, int > const & ) ; + addPerturbableMolecule_function_type addPerturbableMolecule_function_value( &::SireOpenMM::LambdaLever::addPerturbableMolecule ); + + LambdaLever_exposer.def( + "addPerturbableMolecule" + , addPerturbableMolecule_function_value + , ( bp::arg("molecule"), bp::arg("start_indicies") ) + , bp::release_gil_policy() + , "Add info for the passed perturbable OpenMMMolecule, returning\n its index in the list of perturbable molecules\n" ); + + } + { //::SireOpenMM::LambdaLever::addRestraintIndex + + typedef void ( ::SireOpenMM::LambdaLever::*addRestraintIndex_function_type)( ::QString const &,int ) ; + addRestraintIndex_function_type addRestraintIndex_function_value( &::SireOpenMM::LambdaLever::addRestraintIndex ); + + LambdaLever_exposer.def( + "addRestraintIndex" + , addRestraintIndex_function_value + , ( bp::arg("force"), bp::arg("index") ) + , bp::release_gil_policy() + , "Add the index of a restraint force called restraint in the\n OpenMM System. There can be multiple restraint forces with\n the same name\n" ); + + } + { //::SireOpenMM::LambdaLever::getForceIndex + + typedef int ( ::SireOpenMM::LambdaLever::*getForceIndex_function_type)( ::QString const & ) const; + getForceIndex_function_type getForceIndex_function_value( &::SireOpenMM::LambdaLever::getForceIndex ); + + LambdaLever_exposer.def( + "getForceIndex" + , getForceIndex_function_value + , ( bp::arg("name") ) + , bp::release_gil_policy() + , "Get the index of the force called name. Returns -1 if\n there is no force with this name\n" ); + + } + { //::SireOpenMM::LambdaLever::getForceType + + typedef ::QString ( ::SireOpenMM::LambdaLever::*getForceType_function_type)( ::QString const &,::OpenMM::System const & ) const; + getForceType_function_type getForceType_function_value( &::SireOpenMM::LambdaLever::getForceType ); + + LambdaLever_exposer.def( + "getForceType" + , getForceType_function_value + , ( bp::arg("name"), bp::arg("system") ) + , bp::release_gil_policy() + , "Get the C++ type of the force called name. Returns an\n empty string if there is no such force\n" ); + + } + { //::SireOpenMM::LambdaLever::getPerturbableMoleculeMaps + + typedef ::QHash< SireMol::MolNum, SireBase::PropertyMap > ( ::SireOpenMM::LambdaLever::*getPerturbableMoleculeMaps_function_type)( ) const; + getPerturbableMoleculeMaps_function_type getPerturbableMoleculeMaps_function_value( &::SireOpenMM::LambdaLever::getPerturbableMoleculeMaps ); + + LambdaLever_exposer.def( + "getPerturbableMoleculeMaps" + , getPerturbableMoleculeMaps_function_value + , bp::release_gil_policy() + , "Return all of the property maps used to find the perturbable properties\n of the perturbable molecules. This is indexed by molecule number\n" ); + + } + { //::SireOpenMM::LambdaLever::getRestraints + + typedef ::QList< OpenMM::Force * > ( ::SireOpenMM::LambdaLever::*getRestraints_function_type)( ::QString const &,::OpenMM::System & ) const; + getRestraints_function_type getRestraints_function_value( &::SireOpenMM::LambdaLever::getRestraints ); + + LambdaLever_exposer.def( + "getRestraints" + , getRestraints_function_value + , ( bp::arg("name"), bp::arg("system") ) + , bp::release_gil_policy() + , "Return the pointers to all of the forces from the passed System\n are restraints called restraint. This returns an empty\n list if there are no restraints with this name" ); + + } + { //::SireOpenMM::LambdaLever::getSchedule + + typedef ::SireCAS::LambdaSchedule ( ::SireOpenMM::LambdaLever::*getSchedule_function_type)( ) const; + getSchedule_function_type getSchedule_function_value( &::SireOpenMM::LambdaLever::getSchedule ); + + LambdaLever_exposer.def( + "getSchedule" + , getSchedule_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::LambdaLever::hasLever + + typedef bool ( ::SireOpenMM::LambdaLever::*hasLever_function_type)( ::QString const & ) ; + hasLever_function_type hasLever_function_value( &::SireOpenMM::LambdaLever::hasLever ); + + LambdaLever_exposer.def( + "hasLever" + , hasLever_function_value + , ( bp::arg("lever_name") ) + , bp::release_gil_policy() + , "" ); + + } + LambdaLever_exposer.def( bp::self != bp::self ); + { //::SireOpenMM::LambdaLever::operator= + + typedef ::SireOpenMM::LambdaLever & ( ::SireOpenMM::LambdaLever::*assign_function_type)( ::SireOpenMM::LambdaLever const & ) ; + assign_function_type assign_function_value( &::SireOpenMM::LambdaLever::operator= ); + + LambdaLever_exposer.def( + "assign" + , assign_function_value + , ( bp::arg("other") ) + , bp::return_self< >() + , "" ); + + } + LambdaLever_exposer.def( bp::self == bp::self ); + { //::SireOpenMM::LambdaLever::setConstraintIndicies + + typedef void ( ::SireOpenMM::LambdaLever::*setConstraintIndicies_function_type)( int,::QVector< int > const & ) ; + setConstraintIndicies_function_type setConstraintIndicies_function_value( &::SireOpenMM::LambdaLever::setConstraintIndicies ); + + LambdaLever_exposer.def( + "setConstraintIndicies" + , setConstraintIndicies_function_value + , ( bp::arg("idx"), bp::arg("constraint_idxs") ) + , bp::release_gil_policy() + , "Set the constraint indicies for the perturbable molecule at\n index mol_idx\n" ); + + } + { //::SireOpenMM::LambdaLever::setExceptionIndicies + + typedef void ( ::SireOpenMM::LambdaLever::*setExceptionIndicies_function_type)( int,::QString const &,::QVector< boost::tuples::tuple< int, int, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type > > const & ) ; + setExceptionIndicies_function_type setExceptionIndicies_function_value( &::SireOpenMM::LambdaLever::setExceptionIndicies ); + + LambdaLever_exposer.def( + "setExceptionIndicies" + , setExceptionIndicies_function_value + , ( bp::arg("idx"), bp::arg("ff"), bp::arg("exception_idxs") ) + , bp::release_gil_policy() + , "Set the exception indices for the perturbable molecule at\n index mol_idx\n" ); + + } + { //::SireOpenMM::LambdaLever::setForceIndex + + typedef void ( ::SireOpenMM::LambdaLever::*setForceIndex_function_type)( ::QString const &,int ) ; + setForceIndex_function_type setForceIndex_function_value( &::SireOpenMM::LambdaLever::setForceIndex ); + + LambdaLever_exposer.def( + "setForceIndex" + , setForceIndex_function_value + , ( bp::arg("force"), bp::arg("index") ) + , bp::release_gil_policy() + , "Set the index of the force called force in the OpenMM System.\n There can only be one force with this name. Attempts to add\n a duplicate will cause an error to be raised.\n" ); + + } + { //::SireOpenMM::LambdaLever::setLambda + + typedef double ( ::SireOpenMM::LambdaLever::*setLambda_function_type)( ::OpenMM::Context &,double,bool ) const; + setLambda_function_type setLambda_function_value( &::SireOpenMM::LambdaLever::setLambda ); + + LambdaLever_exposer.def( + "setLambda" + , setLambda_function_value + , ( bp::arg("system"), bp::arg("lambda_value"), bp::arg("update_constraints")=(bool)(true) ) + , "Set the value of lambda in the passed context. Returns the\n actual value of lambda set.\n" ); + + } + { //::SireOpenMM::LambdaLever::setSchedule + + typedef void ( ::SireOpenMM::LambdaLever::*setSchedule_function_type)( ::SireCAS::LambdaSchedule const & ) ; + setSchedule_function_type setSchedule_function_value( &::SireOpenMM::LambdaLever::setSchedule ); + + LambdaLever_exposer.def( + "setSchedule" + , setSchedule_function_value + , ( bp::arg("schedule") ) + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::LambdaLever::typeName + + typedef char const * ( *typeName_function_type )( ); + typeName_function_type typeName_function_value( &::SireOpenMM::LambdaLever::typeName ); + + LambdaLever_exposer.def( + "typeName" + , typeName_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::LambdaLever::what + + typedef char const * ( ::SireOpenMM::LambdaLever::*what_function_type)( ) const; + what_function_type what_function_value( &::SireOpenMM::LambdaLever::what ); + + LambdaLever_exposer.def( + "what" + , what_function_value + , bp::release_gil_policy() + , "" ); + + } + LambdaLever_exposer.staticmethod( "typeName" ); + LambdaLever_exposer.def( "__copy__", &__copy__); + LambdaLever_exposer.def( "__deepcopy__", &__copy__); + LambdaLever_exposer.def( "clone", &__copy__); + LambdaLever_exposer.def( "__str__", &__str__< ::SireOpenMM::LambdaLever > ); + LambdaLever_exposer.def( "__repr__", &__str__< ::SireOpenMM::LambdaLever > ); + LambdaLever_exposer.def( "__str__", &__str__< ::SireOpenMM::LambdaLever > ); + LambdaLever_exposer.def( "__repr__", &__str__< ::SireOpenMM::LambdaLever > ); + } + +} diff --git a/wrapper/Convert/SireOpenMM/LambdaLever.pypp.hpp b/wrapper/Convert/SireOpenMM/LambdaLever.pypp.hpp new file mode 100644 index 000000000..e61419093 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/LambdaLever.pypp.hpp @@ -0,0 +1,10 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#ifndef LambdaLever_hpp__pyplusplus_wrapper +#define LambdaLever_hpp__pyplusplus_wrapper + +void register_LambdaLever_class(); + +#endif//LambdaLever_hpp__pyplusplus_wrapper diff --git a/wrapper/Convert/SireOpenMM/OpenMMMetaData.pypp.cpp b/wrapper/Convert/SireOpenMM/OpenMMMetaData.pypp.cpp new file mode 100644 index 000000000..83ce36a68 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/OpenMMMetaData.pypp.cpp @@ -0,0 +1,409 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#include "boost/python.hpp" +#include "OpenMMMetaData.pypp.hpp" + +namespace bp = boost::python; + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +SireOpenMM::OpenMMMetaData __copy__(const SireOpenMM::OpenMMMetaData &other){ return SireOpenMM::OpenMMMetaData(other); } + +#include "Helpers/str.hpp" + +#include "Helpers/release_gil_policy.hpp" + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "Helpers/str.hpp" + +#include "Helpers/release_gil_policy.hpp" + +void register_OpenMMMetaData_class(){ + + { //::SireOpenMM::OpenMMMetaData + typedef bp::class_< SireOpenMM::OpenMMMetaData > OpenMMMetaData_exposer_t; + OpenMMMetaData_exposer_t OpenMMMetaData_exposer = OpenMMMetaData_exposer_t( "OpenMMMetaData", "This is a read-only container for the extra information\nwe need to create an OpenMM system and make it editable\n\nThis is used for a number of things:\n\n1. For an array of\nOpenMM::Vec3 objects (e.g. coordinates or velocities).\nThis is used internally to return coordinate and velocity\ndata up to Python, so that it can be passed back down\nagain to populate the openmm.Context\n\n2. Index - this is a SelectorM in the order that the\natoms appear in the system. This lets us easily locate\natoms by searching\n\n3. LambdaLever - the simplest LambdaLever which can be\nused to update the parameters of the system containing\nperturbable atoms between the two end states\n", bp::init< >("") ); + bp::scope OpenMMMetaData_scope( OpenMMMetaData_exposer ); + OpenMMMetaData_exposer.def( bp::init< SireMol::SelectorM< SireMol::Atom > const &, std::shared_ptr< std::vector< OpenMM::Vec3 > >, std::shared_ptr< std::vector< OpenMM::Vec3 > >, std::shared_ptr< std::vector< OpenMM::Vec3 > >, SireOpenMM::LambdaLever const & >(( bp::arg("atoms"), bp::arg("coords"), bp::arg("vels"), bp::arg("boxvecs"), bp::arg("lever") ), "") ); + { //::SireOpenMM::OpenMMMetaData::boxVectors + + typedef ::std::vector< OpenMM::Vec3 > const & ( ::SireOpenMM::OpenMMMetaData::*boxVectors_function_type)( ) const; + boxVectors_function_type boxVectors_function_value( &::SireOpenMM::OpenMMMetaData::boxVectors ); + + OpenMMMetaData_exposer.def( + "boxVectors" + , boxVectors_function_value + , bp::return_value_policy< bp::copy_const_reference >() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::coordinates + + typedef ::std::vector< OpenMM::Vec3 > const & ( ::SireOpenMM::OpenMMMetaData::*coordinates_function_type)( ) const; + coordinates_function_type coordinates_function_value( &::SireOpenMM::OpenMMMetaData::coordinates ); + + OpenMMMetaData_exposer.def( + "coordinates" + , coordinates_function_value + , bp::return_value_policy< bp::copy_const_reference >() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::hasBoxVectors + + typedef bool ( ::SireOpenMM::OpenMMMetaData::*hasBoxVectors_function_type)( ) const; + hasBoxVectors_function_type hasBoxVectors_function_value( &::SireOpenMM::OpenMMMetaData::hasBoxVectors ); + + OpenMMMetaData_exposer.def( + "hasBoxVectors" + , hasBoxVectors_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::hasCoordinates + + typedef bool ( ::SireOpenMM::OpenMMMetaData::*hasCoordinates_function_type)( ) const; + hasCoordinates_function_type hasCoordinates_function_value( &::SireOpenMM::OpenMMMetaData::hasCoordinates ); + + OpenMMMetaData_exposer.def( + "hasCoordinates" + , hasCoordinates_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::hasVelocities + + typedef bool ( ::SireOpenMM::OpenMMMetaData::*hasVelocities_function_type)( ) const; + hasVelocities_function_type hasVelocities_function_value( &::SireOpenMM::OpenMMMetaData::hasVelocities ); + + OpenMMMetaData_exposer.def( + "hasVelocities" + , hasVelocities_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::index + + typedef ::SireMol::SelectorM< SireMol::Atom > ( ::SireOpenMM::OpenMMMetaData::*index_function_type)( ) const; + index_function_type index_function_value( &::SireOpenMM::OpenMMMetaData::index ); + + OpenMMMetaData_exposer.def( + "index" + , index_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::lambdaLever + + typedef ::SireOpenMM::LambdaLever ( ::SireOpenMM::OpenMMMetaData::*lambdaLever_function_type)( ) const; + lambdaLever_function_type lambdaLever_function_value( &::SireOpenMM::OpenMMMetaData::lambdaLever ); + + OpenMMMetaData_exposer.def( + "lambdaLever" + , lambdaLever_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::toString + + typedef ::QString ( ::SireOpenMM::OpenMMMetaData::*toString_function_type)( ) const; + toString_function_type toString_function_value( &::SireOpenMM::OpenMMMetaData::toString ); + + OpenMMMetaData_exposer.def( + "toString" + , toString_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::typeName + + typedef char const * ( *typeName_function_type )( ); + typeName_function_type typeName_function_value( &::SireOpenMM::OpenMMMetaData::typeName ); + + OpenMMMetaData_exposer.def( + "typeName" + , typeName_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::velocities + + typedef ::std::vector< OpenMM::Vec3 > const & ( ::SireOpenMM::OpenMMMetaData::*velocities_function_type)( ) const; + velocities_function_type velocities_function_value( &::SireOpenMM::OpenMMMetaData::velocities ); + + OpenMMMetaData_exposer.def( + "velocities" + , velocities_function_value + , bp::return_value_policy< bp::copy_const_reference >() + , "" ); + + } + { //::SireOpenMM::OpenMMMetaData::what + + typedef char const * ( ::SireOpenMM::OpenMMMetaData::*what_function_type)( ) const; + what_function_type what_function_value( &::SireOpenMM::OpenMMMetaData::what ); + + OpenMMMetaData_exposer.def( + "what" + , what_function_value + , bp::release_gil_policy() + , "" ); + + } + OpenMMMetaData_exposer.staticmethod( "typeName" ); + OpenMMMetaData_exposer.def( "__copy__", &__copy__); + OpenMMMetaData_exposer.def( "__deepcopy__", &__copy__); + OpenMMMetaData_exposer.def( "clone", &__copy__); + OpenMMMetaData_exposer.def( "__str__", &__str__< ::SireOpenMM::OpenMMMetaData > ); + OpenMMMetaData_exposer.def( "__repr__", &__str__< ::SireOpenMM::OpenMMMetaData > ); + OpenMMMetaData_exposer.def( "__str__", &__str__< ::SireOpenMM::OpenMMMetaData > ); + OpenMMMetaData_exposer.def( "__repr__", &__str__< ::SireOpenMM::OpenMMMetaData > ); + } + +} diff --git a/wrapper/Convert/SireOpenMM/OpenMMMetaData.pypp.hpp b/wrapper/Convert/SireOpenMM/OpenMMMetaData.pypp.hpp new file mode 100644 index 000000000..be3eb7178 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/OpenMMMetaData.pypp.hpp @@ -0,0 +1,10 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#ifndef OpenMMMetaData_hpp__pyplusplus_wrapper +#define OpenMMMetaData_hpp__pyplusplus_wrapper + +void register_OpenMMMetaData_class(); + +#endif//OpenMMMetaData_hpp__pyplusplus_wrapper diff --git a/wrapper/Convert/SireOpenMM/PerturbableOpenMMMolecule.pypp.cpp b/wrapper/Convert/SireOpenMM/PerturbableOpenMMMolecule.pypp.cpp new file mode 100644 index 000000000..7d1bf77c3 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/PerturbableOpenMMMolecule.pypp.cpp @@ -0,0 +1,798 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#include "boost/python.hpp" +#include "PerturbableOpenMMMolecule.pypp.hpp" + +namespace bp = boost::python; + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMM/twoatomfunctions.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireUnits/units.h" + +#include "openmmmolecule.h" + +#include "tostring.h" + +#include + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMM/twoatomfunctions.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireUnits/units.h" + +#include "openmmmolecule.h" + +#include "tostring.h" + +#include + +#include + +#include + +SireOpenMM::PerturbableOpenMMMolecule __copy__(const SireOpenMM::PerturbableOpenMMMolecule &other){ return SireOpenMM::PerturbableOpenMMMolecule(other); } + +#include "Helpers/str.hpp" + +#include "Helpers/release_gil_policy.hpp" + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMM/twoatomfunctions.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireUnits/units.h" + +#include "openmmmolecule.h" + +#include "tostring.h" + +#include + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMM/twoatomfunctions.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireUnits/units.h" + +#include "openmmmolecule.h" + +#include "tostring.h" + +#include + +#include + +#include + +#include "Helpers/str.hpp" + +#include "Helpers/release_gil_policy.hpp" + +void register_PerturbableOpenMMMolecule_class(){ + + { //::SireOpenMM::PerturbableOpenMMMolecule + typedef bp::class_< SireOpenMM::PerturbableOpenMMMolecule > PerturbableOpenMMMolecule_exposer_t; + PerturbableOpenMMMolecule_exposer_t PerturbableOpenMMMolecule_exposer = PerturbableOpenMMMolecule_exposer_t( "PerturbableOpenMMMolecule", "This class holds all of the information of an OpenMM molecule\nthat can be perturbed using a LambdaSchedule. The data is held\nin easy-to-access arrays, with guarantees that the arrays are\ncompatible and the data is aligned.\n", bp::init< >("Null constructor") ); + bp::scope PerturbableOpenMMMolecule_scope( PerturbableOpenMMMolecule_exposer ); + PerturbableOpenMMMolecule_exposer.def( bp::init< SireOpenMM::OpenMMMolecule const & >(( bp::arg("mol") ), "Construct from the passed OpenMMMolecule") ); + PerturbableOpenMMMolecule_exposer.def( bp::init< SireMol::Molecule const &, SireBase::PropertyMap const & >(( bp::arg("mol"), bp::arg("map") ), "") ); + PerturbableOpenMMMolecule_exposer.def( bp::init< SireOpenMM::PerturbableOpenMMMolecule const & >(( bp::arg("other") ), "Copy constructor") ); + { //::SireOpenMM::PerturbableOpenMMMolecule::angles + + typedef ::QList< SireMM::Angle > ( ::SireOpenMM::PerturbableOpenMMMolecule::*angles_function_type)( ) const; + angles_function_type angles_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::angles ); + + PerturbableOpenMMMolecule_exposer.def( + "angles" + , angles_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::atoms + + typedef ::QList< SireMol::Atom > ( ::SireOpenMM::PerturbableOpenMMMolecule::*atoms_function_type)( ) const; + atoms_function_type atoms_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::atoms ); + + PerturbableOpenMMMolecule_exposer.def( + "atoms" + , atoms_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::bonds + + typedef ::QList< SireMM::Bond > ( ::SireOpenMM::PerturbableOpenMMMolecule::*bonds_function_type)( ) const; + bonds_function_type bonds_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::bonds ); + + PerturbableOpenMMMolecule_exposer.def( + "bonds" + , bonds_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getAlphas0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getAlphas0_function_type)( ) const; + getAlphas0_function_type getAlphas0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getAlphas0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getAlphas0" + , getAlphas0_function_value + , bp::release_gil_policy() + , "Return the alpha parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getAlphas1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getAlphas1_function_type)( ) const; + getAlphas1_function_type getAlphas1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getAlphas1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getAlphas1" + , getAlphas1_function_value + , bp::release_gil_policy() + , "Return the alpha parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getAngleKs0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getAngleKs0_function_type)( ) const; + getAngleKs0_function_type getAngleKs0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getAngleKs0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getAngleKs0" + , getAngleKs0_function_value + , bp::release_gil_policy() + , "Return the angle k parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getAngleKs1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getAngleKs1_function_type)( ) const; + getAngleKs1_function_type getAngleKs1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getAngleKs1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getAngleKs1" + , getAngleKs1_function_value + , bp::release_gil_policy() + , "Return the angle k parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getAngleSizes0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getAngleSizes0_function_type)( ) const; + getAngleSizes0_function_type getAngleSizes0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getAngleSizes0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getAngleSizes0" + , getAngleSizes0_function_value + , bp::release_gil_policy() + , "Return the angle size parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getAngleSizes1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getAngleSizes1_function_type)( ) const; + getAngleSizes1_function_type getAngleSizes1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getAngleSizes1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getAngleSizes1" + , getAngleSizes1_function_value + , bp::release_gil_policy() + , "Return the angle size parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getBondKs0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getBondKs0_function_type)( ) const; + getBondKs0_function_type getBondKs0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getBondKs0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getBondKs0" + , getBondKs0_function_value + , bp::release_gil_policy() + , "Return the bond k parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getBondKs1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getBondKs1_function_type)( ) const; + getBondKs1_function_type getBondKs1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getBondKs1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getBondKs1" + , getBondKs1_function_value + , bp::release_gil_policy() + , "Return the bond k parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getBondLengths0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getBondLengths0_function_type)( ) const; + getBondLengths0_function_type getBondLengths0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getBondLengths0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getBondLengths0" + , getBondLengths0_function_value + , bp::release_gil_policy() + , "Return the bond length parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getBondLengths1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getBondLengths1_function_type)( ) const; + getBondLengths1_function_type getBondLengths1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getBondLengths1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getBondLengths1" + , getBondLengths1_function_value + , bp::release_gil_policy() + , "Return the bond length parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getChargeScales0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getChargeScales0_function_type)( ) const; + getChargeScales0_function_type getChargeScales0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getChargeScales0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getChargeScales0" + , getChargeScales0_function_value + , bp::release_gil_policy() + , "Return the coulomb intramolecular scale factors of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getChargeScales1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getChargeScales1_function_type)( ) const; + getChargeScales1_function_type getChargeScales1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getChargeScales1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getChargeScales1" + , getChargeScales1_function_value + , bp::release_gil_policy() + , "Return the coulomb intramolecular scale factors of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getCharges0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getCharges0_function_type)( ) const; + getCharges0_function_type getCharges0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getCharges0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getCharges0" + , getCharges0_function_value + , bp::release_gil_policy() + , "Return the atom charges of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getCharges1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getCharges1_function_type)( ) const; + getCharges1_function_type getCharges1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getCharges1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getCharges1" + , getCharges1_function_value + , bp::release_gil_policy() + , "Return the atom charges of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getConstraintIndicies + + typedef ::QVector< int > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getConstraintIndicies_function_type)( ) const; + getConstraintIndicies_function_type getConstraintIndicies_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getConstraintIndicies ); + + PerturbableOpenMMMolecule_exposer.def( + "getConstraintIndicies" + , getConstraintIndicies_function_value + , bp::release_gil_policy() + , "Return the indicies of the perturbable constraints" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getEpsilons0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getEpsilons0_function_type)( ) const; + getEpsilons0_function_type getEpsilons0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getEpsilons0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getEpsilons0" + , getEpsilons0_function_value + , bp::release_gil_policy() + , "Return the LJ epsilon parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getEpsilons1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getEpsilons1_function_type)( ) const; + getEpsilons1_function_type getEpsilons1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getEpsilons1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getEpsilons1" + , getEpsilons1_function_value + , bp::release_gil_policy() + , "Return the LJ epsilon parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getExceptionAtoms + + typedef ::QVector< boost::tuples::tuple< int, int, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type > > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getExceptionAtoms_function_type)( ) const; + getExceptionAtoms_function_type getExceptionAtoms_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getExceptionAtoms ); + + PerturbableOpenMMMolecule_exposer.def( + "getExceptionAtoms" + , getExceptionAtoms_function_value + , bp::release_gil_policy() + , "Return the indices of the atoms in the exceptions" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getExceptionIndicies + + typedef ::QVector< boost::tuples::tuple< int, int, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type > > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getExceptionIndicies_function_type)( ::QString const & ) const; + getExceptionIndicies_function_type getExceptionIndicies_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getExceptionIndicies ); + + PerturbableOpenMMMolecule_exposer.def( + "getExceptionIndicies" + , getExceptionIndicies_function_value + , ( bp::arg("name") ) + , bp::release_gil_policy() + , "Return the global indexes of the exceptions in the non-bonded and\n ghost-14 forces\n" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getFromGhostIdxs + + typedef ::QSet< int > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getFromGhostIdxs_function_type)( ) const; + getFromGhostIdxs_function_type getFromGhostIdxs_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getFromGhostIdxs ); + + PerturbableOpenMMMolecule_exposer.def( + "getFromGhostIdxs" + , getFromGhostIdxs_function_value + , bp::release_gil_policy() + , "Return the indexes of the atoms that were ghosts in the\n reference state\n" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getKappas0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getKappas0_function_type)( ) const; + getKappas0_function_type getKappas0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getKappas0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getKappas0" + , getKappas0_function_value + , bp::release_gil_policy() + , "Return the kappa parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getKappas1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getKappas1_function_type)( ) const; + getKappas1_function_type getKappas1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getKappas1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getKappas1" + , getKappas1_function_value + , bp::release_gil_policy() + , "Return the kappa parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getLJScales0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getLJScales0_function_type)( ) const; + getLJScales0_function_type getLJScales0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getLJScales0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getLJScales0" + , getLJScales0_function_value + , bp::release_gil_policy() + , "Return the LJ intramolecular scale factors of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getLJScales1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getLJScales1_function_type)( ) const; + getLJScales1_function_type getLJScales1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getLJScales1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getLJScales1" + , getLJScales1_function_value + , bp::release_gil_policy() + , "Return the LJ intramolecular scale factors of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getPerturbableConstraints + + typedef ::boost::tuples::tuple< QVector< int >, QVector< double >, QVector< double >, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getPerturbableConstraints_function_type)( ) const; + getPerturbableConstraints_function_type getPerturbableConstraints_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getPerturbableConstraints ); + + PerturbableOpenMMMolecule_exposer.def( + "getPerturbableConstraints" + , getPerturbableConstraints_function_value + , bp::release_gil_policy() + , "Return three arrays containing the constraint indexes, and the\n reference and perturbed values of the constraint lengths\n" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getPerturbableConstraintsWithAtoms + + typedef ::QVector< boost::tuples::tuple< int, int, double, double, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type > > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getPerturbableConstraintsWithAtoms_function_type)( ) const; + getPerturbableConstraintsWithAtoms_function_type getPerturbableConstraintsWithAtoms_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getPerturbableConstraintsWithAtoms ); + + PerturbableOpenMMMolecule_exposer.def( + "getPerturbableConstraintsWithAtoms" + , getPerturbableConstraintsWithAtoms_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getSigmas0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getSigmas0_function_type)( ) const; + getSigmas0_function_type getSigmas0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getSigmas0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getSigmas0" + , getSigmas0_function_value + , bp::release_gil_policy() + , "Return the LJ sigma parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getSigmas1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getSigmas1_function_type)( ) const; + getSigmas1_function_type getSigmas1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getSigmas1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getSigmas1" + , getSigmas1_function_value + , bp::release_gil_policy() + , "Return the LJ sigma parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getToGhostIdxs + + typedef ::QSet< int > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getToGhostIdxs_function_type)( ) const; + getToGhostIdxs_function_type getToGhostIdxs_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getToGhostIdxs ); + + PerturbableOpenMMMolecule_exposer.def( + "getToGhostIdxs" + , getToGhostIdxs_function_value + , bp::release_gil_policy() + , "Return the indexes of the atoms that are to be ghosted in the\n perturbed state\n" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getTorsionKs0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getTorsionKs0_function_type)( ) const; + getTorsionKs0_function_type getTorsionKs0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getTorsionKs0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getTorsionKs0" + , getTorsionKs0_function_value + , bp::release_gil_policy() + , "Return the torsion k parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getTorsionKs1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getTorsionKs1_function_type)( ) const; + getTorsionKs1_function_type getTorsionKs1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getTorsionKs1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getTorsionKs1" + , getTorsionKs1_function_value + , bp::release_gil_policy() + , "Return the torsion k parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getTorsionPeriodicities0 + + typedef ::QVector< signed char > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getTorsionPeriodicities0_function_type)( ) const; + getTorsionPeriodicities0_function_type getTorsionPeriodicities0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getTorsionPeriodicities0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getTorsionPeriodicities0" + , getTorsionPeriodicities0_function_value + , bp::release_gil_policy() + , "Return the torsion periodicity parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getTorsionPeriodicities1 + + typedef ::QVector< signed char > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getTorsionPeriodicities1_function_type)( ) const; + getTorsionPeriodicities1_function_type getTorsionPeriodicities1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getTorsionPeriodicities1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getTorsionPeriodicities1" + , getTorsionPeriodicities1_function_value + , bp::release_gil_policy() + , "Return the torsion periodicity parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getTorsionPhases0 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getTorsionPhases0_function_type)( ) const; + getTorsionPhases0_function_type getTorsionPhases0_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getTorsionPhases0 ); + + PerturbableOpenMMMolecule_exposer.def( + "getTorsionPhases0" + , getTorsionPhases0_function_value + , bp::release_gil_policy() + , "Return the torsion phase parameters of the reference state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::getTorsionPhases1 + + typedef ::QVector< double > ( ::SireOpenMM::PerturbableOpenMMMolecule::*getTorsionPhases1_function_type)( ) const; + getTorsionPhases1_function_type getTorsionPhases1_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::getTorsionPhases1 ); + + PerturbableOpenMMMolecule_exposer.def( + "getTorsionPhases1" + , getTorsionPhases1_function_value + , bp::release_gil_policy() + , "Return the torsion phase parameters of the perturbed state" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::isGhostAtom + + typedef bool ( ::SireOpenMM::PerturbableOpenMMMolecule::*isGhostAtom_function_type)( int ) const; + isGhostAtom_function_type isGhostAtom_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::isGhostAtom ); + + PerturbableOpenMMMolecule_exposer.def( + "isGhostAtom" + , isGhostAtom_function_value + , ( bp::arg("atom") ) + , bp::release_gil_policy() + , "Return true if the atom is a ghost atom in the\n referenece or perturbed states" ); + + } + PerturbableOpenMMMolecule_exposer.def( bp::self != bp::self ); + { //::SireOpenMM::PerturbableOpenMMMolecule::operator= + + typedef ::SireOpenMM::PerturbableOpenMMMolecule & ( ::SireOpenMM::PerturbableOpenMMMolecule::*assign_function_type)( ::SireOpenMM::PerturbableOpenMMMolecule const & ) ; + assign_function_type assign_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::operator= ); + + PerturbableOpenMMMolecule_exposer.def( + "assign" + , assign_function_value + , ( bp::arg("other") ) + , bp::return_self< >() + , "" ); + + } + PerturbableOpenMMMolecule_exposer.def( bp::self == bp::self ); + { //::SireOpenMM::PerturbableOpenMMMolecule::setConstraintIndicies + + typedef void ( ::SireOpenMM::PerturbableOpenMMMolecule::*setConstraintIndicies_function_type)( ::QVector< int > const & ) ; + setConstraintIndicies_function_type setConstraintIndicies_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::setConstraintIndicies ); + + PerturbableOpenMMMolecule_exposer.def( + "setConstraintIndicies" + , setConstraintIndicies_function_value + , ( bp::arg("constraint_idxs") ) + , bp::release_gil_policy() + , "Set the indexes of perturbable constraints in the System" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::setExceptionIndicies + + typedef void ( ::SireOpenMM::PerturbableOpenMMMolecule::*setExceptionIndicies_function_type)( ::QString const &,::QVector< boost::tuples::tuple< int, int, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type > > const & ) ; + setExceptionIndicies_function_type setExceptionIndicies_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::setExceptionIndicies ); + + PerturbableOpenMMMolecule_exposer.def( + "setExceptionIndicies" + , setExceptionIndicies_function_value + , ( bp::arg("name"), bp::arg("exception_idxs") ) + , bp::release_gil_policy() + , "Set the global indexes of the exceptions in the non-bonded and\n ghost-14 forces\n" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::toString + + typedef ::QString ( ::SireOpenMM::PerturbableOpenMMMolecule::*toString_function_type)( ) const; + toString_function_type toString_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::toString ); + + PerturbableOpenMMMolecule_exposer.def( + "toString" + , toString_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::torsions + + typedef ::QList< SireMM::Dihedral > ( ::SireOpenMM::PerturbableOpenMMMolecule::*torsions_function_type)( ) const; + torsions_function_type torsions_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::torsions ); + + PerturbableOpenMMMolecule_exposer.def( + "torsions" + , torsions_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::typeName + + typedef char const * ( *typeName_function_type )( ); + typeName_function_type typeName_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::typeName ); + + PerturbableOpenMMMolecule_exposer.def( + "typeName" + , typeName_function_value + , bp::release_gil_policy() + , "" ); + + } + { //::SireOpenMM::PerturbableOpenMMMolecule::what + + typedef char const * ( ::SireOpenMM::PerturbableOpenMMMolecule::*what_function_type)( ) const; + what_function_type what_function_value( &::SireOpenMM::PerturbableOpenMMMolecule::what ); + + PerturbableOpenMMMolecule_exposer.def( + "what" + , what_function_value + , bp::release_gil_policy() + , "" ); + + } + PerturbableOpenMMMolecule_exposer.staticmethod( "typeName" ); + PerturbableOpenMMMolecule_exposer.def( "__copy__", &__copy__); + PerturbableOpenMMMolecule_exposer.def( "__deepcopy__", &__copy__); + PerturbableOpenMMMolecule_exposer.def( "clone", &__copy__); + PerturbableOpenMMMolecule_exposer.def( "__str__", &__str__< ::SireOpenMM::PerturbableOpenMMMolecule > ); + PerturbableOpenMMMolecule_exposer.def( "__repr__", &__str__< ::SireOpenMM::PerturbableOpenMMMolecule > ); + PerturbableOpenMMMolecule_exposer.def( "__str__", &__str__< ::SireOpenMM::PerturbableOpenMMMolecule > ); + PerturbableOpenMMMolecule_exposer.def( "__repr__", &__str__< ::SireOpenMM::PerturbableOpenMMMolecule > ); + } + +} diff --git a/wrapper/Convert/SireOpenMM/PerturbableOpenMMMolecule.pypp.hpp b/wrapper/Convert/SireOpenMM/PerturbableOpenMMMolecule.pypp.hpp new file mode 100644 index 000000000..663a0beb3 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/PerturbableOpenMMMolecule.pypp.hpp @@ -0,0 +1,10 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#ifndef PerturbableOpenMMMolecule_hpp__pyplusplus_wrapper +#define PerturbableOpenMMMolecule_hpp__pyplusplus_wrapper + +void register_PerturbableOpenMMMolecule_class(); + +#endif//PerturbableOpenMMMolecule_hpp__pyplusplus_wrapper diff --git a/wrapper/Convert/SireOpenMM/SireOpenMM_registrars.cpp b/wrapper/Convert/SireOpenMM/SireOpenMM_registrars.cpp new file mode 100644 index 000000000..de06e3b14 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/SireOpenMM_registrars.cpp @@ -0,0 +1,20 @@ +//WARNING - AUTOGENERATED FILE - CONTENTS WILL BE OVERWRITTEN! +#include + +#include "SireOpenMM_registrars.h" + +#include "openmmmolecule.h" +#include "lambdalever.h" + +#include "Helpers/objectregistry.hpp" + +void register_SireOpenMM_objects() +{ + + ObjectRegistry::registerConverterFor< SireOpenMM::PerturbableOpenMMMolecule >(); + ObjectRegistry::registerConverterFor< SireOpenMM::PerturbableOpenMMMolecule >(); + ObjectRegistry::registerConverterFor< SireOpenMM::LambdaLever >(); + ObjectRegistry::registerConverterFor< SireOpenMM::LambdaLever >(); + +} + diff --git a/wrapper/Convert/SireOpenMM/SireOpenMM_registrars.h b/wrapper/Convert/SireOpenMM/SireOpenMM_registrars.h new file mode 100644 index 000000000..73fb31820 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/SireOpenMM_registrars.h @@ -0,0 +1,6 @@ +//WARNING - AUTOGENERATED FILE - CONTENTS WILL BE OVERWRITTEN! +#ifndef PYWRAP_SireOpenMM_REGISTRARS_H +#define PYWRAP_SireOpenMM_REGISTRARS_H +void register_SireOpenMM_objects(); +#endif + diff --git a/wrapper/Convert/SireOpenMM/_SireOpenMM.main.cpp b/wrapper/Convert/SireOpenMM/_SireOpenMM.main.cpp index 0757bc9bc..63aa4e9fd 100644 --- a/wrapper/Convert/SireOpenMM/_SireOpenMM.main.cpp +++ b/wrapper/Convert/SireOpenMM/_SireOpenMM.main.cpp @@ -1,129 +1,41 @@ +// This file has been generated by Py++. // (C) Christopher Woods, GPL >= 3 License + #include "boost/python.hpp" -#include "sire_openmm.h" +#include "boost/python/suite/indexing/vector_indexing_suite.hpp" + +#include "LambdaLever.pypp.hpp" -#include "lambdalever.h" +#include "OpenMMMetaData.pypp.hpp" -#include "openmmminimise.h" +#include "PerturbableOpenMMMolecule.pypp.hpp" -#include "Helpers/convertlist.hpp" +#include "_SireOpenMM_free_functions.pypp.hpp" -#include +#include "vector_less__OpenMM_scope_Vec3__greater_.pypp.hpp" namespace bp = boost::python; -using namespace SireOpenMM; - -/** Thanks to this page for instructions on how to convert from SWIG to Boost - * https://wiki.python.org/moin/boost.python/HowTo - */ -struct PySwigObject -{ - PyObject_HEAD void *ptr; - const char *desc; -}; - -void *extract_swig_wrapped_pointer(PyObject *obj) -{ - char thisStr[] = "this"; - - // first we need to get the this attribute from the Python Object - if (!PyObject_HasAttrString(obj, thisStr)) - return NULL; - - PyObject *thisAttr = PyObject_GetAttrString(obj, thisStr); - if (thisAttr == NULL) - return NULL; - - // This Python Object is a SWIG Wrapper and contains our pointer - void *pointer = ((PySwigObject *)thisAttr)->ptr; - Py_DECREF(thisAttr); - return pointer; -} +#include "SireOpenMM_registrars.h" + +#include "./register_extras.h" + +BOOST_PYTHON_MODULE(_SireOpenMM){ + register_SireOpenMM_objects(); + + register_vector_less__OpenMM_scope_Vec3__greater__class(); -BOOST_PYTHON_MODULE(_SireOpenMM) -{ - bp::class_ OpenMMMetaData_exposer_t("OpenMMMetaData", - "Internal class used to hold OpenMM coordinates and velocities data"); - - OpenMMMetaData_exposer_t.def( - "index", &OpenMMMetaData::index, - "Return the index used to locate atoms in the OpenMM system"); - - OpenMMMetaData_exposer_t.def( - "lambdaLever", &OpenMMMetaData::lambdaLever, - "Return the lambda lever used to update the parameters in the " - "OpenMM system according to lambda"); - - bp::class_> LambdaLever_exposer_t( - "LambdaLever", - "A lever that can be used to change the parameters in an OpenMM system " - "based on a lambda value (or collection of lambda values)"); - - LambdaLever_exposer_t.def( - "set_lambda", &LambdaLever::setLambda, - (bp::arg("system"), bp::arg("lambda_value")), - "Update the parameters in the passed context using this lambda lever " - "so that the parameters represent the system at the specified " - "lambda value"); - - LambdaLever_exposer_t.def( - "schedule", &LambdaLever::getSchedule, - "Return the LambdaSchedule used to control the parameters by lambda"); - - LambdaLever_exposer_t.def( - "set_schedule", &LambdaLever::setSchedule, - "Set the LambdaSchedule used to control the parameters by lambda"); - - LambdaLever_exposer_t.def( - "get_perturbable_molecule_maps", &LambdaLever::getPerturbableMoleculeMaps, - "Return the perturbable molecule maps for all of the perturbable molecules"); - - bp::def("_openmm_system_to_sire", - &openmm_system_to_sire, - (bp::arg("system"), bp::arg("map")), - "Convert an OpenMM::System to a set of sire molecules."); - - bp::def("_sire_to_openmm_system", - &sire_to_openmm_system, - (bp::arg("system"), bp::arg("mols"), bp::arg("map")), - "Convert sire molecules to an OpenMM::System"); - - bp::def("_set_openmm_coordinates_and_velocities", - &set_openmm_coordinates_and_velocities, - (bp::arg("context"), bp::arg("coords_and_velocities")), - "Set the coordinates and velocities in a context"); - - bp::def("_openmm_extract_coordinates", - &extract_coordinates, - (bp::arg("state"), bp::arg("mols"), bp::arg("perturbable_maps"), bp::arg("map")), - "Extract the coordinates from 'state' and copy then into the passed 'mols'"); - - bp::def("_openmm_extract_coordinates_and_velocities", - &extract_coordinates_and_velocities, - (bp::arg("state"), bp::arg("mols"), bp::arg("perturbable_maps"), bp::arg("map")), - "Extract the coordinates and velocities from 'state' and copy then into the passed 'mols'"); - - bp::def("_openmm_extract_space", - &extract_space, - (bp::arg("state")), - "Extract and return the space from 'state'"); - - bp::def("_openmm_set_context_platform_property", - &set_context_platform_property, - (bp::arg("context"), bp::arg("key"), bp::arg("value")), - "Set the Platform property for the passed context."); - - bp::def("_minimise_openmm_context", - &minimise_openmm_context, - (bp::arg("context"), bp::arg("tolerance") = 10, bp::arg("max_iterations") = -1), - "Minimise the passed context"); - - bp::converter::registry::insert(&extract_swig_wrapped_pointer, bp::type_id()); - bp::converter::registry::insert(&extract_swig_wrapped_pointer, bp::type_id()); - bp::converter::registry::insert(&extract_swig_wrapped_pointer, bp::type_id()); - bp::converter::registry::insert(&extract_swig_wrapped_pointer, bp::type_id()); + register_LambdaLever_class(); + + register_OpenMMMetaData_class(); + + register_PerturbableOpenMMMolecule_class(); + + SireOpenMM::register_extras(); + + register_free_functions(); } + diff --git a/wrapper/Convert/SireOpenMM/_SireOpenMM_free_functions.pypp.cpp b/wrapper/Convert/SireOpenMM/_SireOpenMM_free_functions.pypp.cpp new file mode 100644 index 000000000..cc46c3d10 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/_SireOpenMM_free_functions.pypp.cpp @@ -0,0 +1,2169 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#include "boost/python.hpp" +#include "_SireOpenMM_free_functions.pypp.hpp" + +namespace bp = boost::python; + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/progressbar.h" + +#include "SireBase/releasegil.h" + +#include "SireError/errors.h" + +#include "SireUnits/units.h" + +#include "lbgfs/lbfgs.h" + +#include "openmm/OpenMMException.h" + +#include "openmm/Platform.h" + +#include "openmm/VerletIntegrator.h" + +#include "openmmminimise.h" + +#include + +#include + +#include + +#include + +#include + +#include + +#include "SireBase/progressbar.h" + +#include "SireBase/releasegil.h" + +#include "SireError/errors.h" + +#include "SireUnits/units.h" + +#include "lbgfs/lbfgs.h" + +#include "openmm/OpenMMException.h" + +#include "openmm/Platform.h" + +#include "openmm/VerletIntegrator.h" + +#include "openmmminimise.h" + +#include + +#include + +#include + +#include + +#include + +#include + +#include "SireBase/progressbar.h" + +#include "SireBase/releasegil.h" + +#include "SireError/errors.h" + +#include "SireUnits/units.h" + +#include "lbgfs/lbfgs.h" + +#include "openmm/OpenMMException.h" + +#include "openmm/Platform.h" + +#include "openmm/VerletIntegrator.h" + +#include "openmmminimise.h" + +#include + +#include + +#include + +#include + +#include + +#include + +#include "SireBase/progressbar.h" + +#include "SireBase/releasegil.h" + +#include "SireError/errors.h" + +#include "SireUnits/units.h" + +#include "lbgfs/lbfgs.h" + +#include "openmm/OpenMMException.h" + +#include "openmm/Platform.h" + +#include "openmm/VerletIntegrator.h" + +#include "openmmminimise.h" + +#include + +#include + +#include + +#include + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +#include "SireBase/parallel.h" + +#include "SireBase/propertylist.h" + +#include "SireCAS/lambdaschedule.h" + +#include "SireError/errors.h" + +#include "SireMM/amberparams.h" + +#include "SireMM/atomljs.h" + +#include "SireMM/selectorbond.h" + +#include "SireMaths/vector.h" + +#include "SireMol/atomcharges.h" + +#include "SireMol/atomcoords.h" + +#include "SireMol/atomelements.h" + +#include "SireMol/atommasses.h" + +#include "SireMol/atomproperty.hpp" + +#include "SireMol/atomvelocities.h" + +#include "SireMol/bondid.h" + +#include "SireMol/bondorder.h" + +#include "SireMol/connectivity.h" + +#include "SireMol/core.h" + +#include "SireMol/moleditor.h" + +#include "SireStream/datastream.h" + +#include "SireStream/shareddatastream.h" + +#include "SireSystem/forcefieldinfo.h" + +#include "SireUnits/units.h" + +#include "SireVol/periodicbox.h" + +#include "SireVol/triclinicbox.h" + +#include "openmmmolecule.h" + +#include "sire_openmm.h" + +#include "tostring.h" + +#include + +#include + +void register_free_functions(){ + + { //::SireOpenMM::extract_coordinates + + typedef ::SireMol::SelectorMol ( *extract_coordinates_function_type )( ::OpenMM::State const &,::SireMol::SelectorMol const &,::QHash< SireMol::MolNum, SireBase::PropertyMap > const &,::SireBase::PropertyMap const & ); + extract_coordinates_function_type extract_coordinates_function_value( &::SireOpenMM::extract_coordinates ); + + bp::def( + "extract_coordinates" + , extract_coordinates_function_value + , ( bp::arg("state"), bp::arg("mols"), bp::arg("perturbable_maps"), bp::arg("map") ) + , "" ); + + } + + { //::SireOpenMM::extract_coordinates_and_velocities + + typedef ::SireMol::SelectorMol ( *extract_coordinates_and_velocities_function_type )( ::OpenMM::State const &,::SireMol::SelectorMol const &,::QHash< SireMol::MolNum, SireBase::PropertyMap > const &,::SireBase::PropertyMap const & ); + extract_coordinates_and_velocities_function_type extract_coordinates_and_velocities_function_value( &::SireOpenMM::extract_coordinates_and_velocities ); + + bp::def( + "extract_coordinates_and_velocities" + , extract_coordinates_and_velocities_function_value + , ( bp::arg("state"), bp::arg("mols"), bp::arg("perturbable_maps"), bp::arg("map") ) + , "" ); + + } + + { //::SireOpenMM::extract_space + + typedef ::SireVol::SpacePtr ( *extract_space_function_type )( ::OpenMM::State const & ); + extract_space_function_type extract_space_function_value( &::SireOpenMM::extract_space ); + + bp::def( + "extract_space" + , extract_space_function_value + , ( bp::arg("state") ) + , "" ); + + } + + { //::SireOpenMM::get_potential_energy + + typedef ::SireUnits::Dimension::MolarEnergy ( *get_potential_energy_function_type )( ::OpenMM::Context & ); + get_potential_energy_function_type get_potential_energy_function_value( &::SireOpenMM::get_potential_energy ); + + bp::def( + "get_potential_energy" + , get_potential_energy_function_value + , ( bp::arg("context") ) + , "" ); + + } + + { //::SireOpenMM::minimise_openmm_context + + typedef void ( *minimise_openmm_context_function_type )( ::OpenMM::Context &,double,int ); + minimise_openmm_context_function_type minimise_openmm_context_function_value( &::SireOpenMM::minimise_openmm_context ); + + bp::def( + "minimise_openmm_context" + , minimise_openmm_context_function_value + , ( bp::arg("context"), bp::arg("tolerance")=10, bp::arg("max_iterations")=(int)(-1) ) + , "This is a minimiser heavily inspired by the\nLocalEnergyMinimizer included in OpenMM. This is re-written\nfor sire to;\n\n1. Better integrate minimisation into the sire progress\nmonitoring interupting framework.\n2. Avoid errors caused by OpenMM switching from the desired\ncontext to the CPU context, thus triggering spurious exceptions\nrelated to exclusions exceptions not matching\n" ); + + } + + { //::SireOpenMM::openmm_system_to_sire + + typedef ::SireMol::SelectorMol ( *openmm_system_to_sire_function_type )( ::OpenMM::System const &,::SireBase::PropertyMap const & ); + openmm_system_to_sire_function_type openmm_system_to_sire_function_value( &::SireOpenMM::openmm_system_to_sire ); + + bp::def( + "openmm_system_to_sire" + , openmm_system_to_sire_function_value + , ( bp::arg("system"), bp::arg("map") ) + , "" ); + + } + + { //::SireOpenMM::set_context_platform_property + + typedef void ( *set_context_platform_property_function_type )( ::OpenMM::Context &,::QString const &,::QString const & ); + set_context_platform_property_function_type set_context_platform_property_function_value( &::SireOpenMM::set_context_platform_property ); + + bp::def( + "set_context_platform_property" + , set_context_platform_property_function_value + , ( bp::arg("context"), bp::arg("key"), bp::arg("value") ) + , "" ); + + } + + { //::SireOpenMM::set_openmm_coordinates_and_velocities + + typedef void ( *set_openmm_coordinates_and_velocities_function_type )( ::OpenMM::Context &,::SireOpenMM::OpenMMMetaData const & ); + set_openmm_coordinates_and_velocities_function_type set_openmm_coordinates_and_velocities_function_value( &::SireOpenMM::set_openmm_coordinates_and_velocities ); + + bp::def( + "set_openmm_coordinates_and_velocities" + , set_openmm_coordinates_and_velocities_function_value + , ( bp::arg("context"), bp::arg("coords_and_velocities") ) + , "" ); + + } + + { //::SireOpenMM::sire_to_openmm_system + + typedef ::SireOpenMM::OpenMMMetaData ( *sire_to_openmm_system_function_type )( ::OpenMM::System &,::SireMol::SelectorMol const &,::SireBase::PropertyMap const & ); + sire_to_openmm_system_function_type sire_to_openmm_system_function_value( &::SireOpenMM::sire_to_openmm_system ); + + bp::def( + "sire_to_openmm_system" + , sire_to_openmm_system_function_value + , ( bp::arg("system"), bp::arg("mols"), bp::arg("map") ) + , "\nThis is the (monster) function that converts a passed set of Sire\nmolecules (in the passed SelectorMols) into an OpenMM::System,\ncontrolled via the properties in the passed PropertyMap.\nThe OpenMM::System is constructed in the passed (empty)\nOpenMM::System that is the first argument. This is because this\nfunction is called from Python, and this was the only way found to\nhave the resulting OpenMM::System make its way back up to the\nPython layer.\nThis returns an extra set of metadata that doesnt fit into the\nOpenMM::System. This metadata includes information about any\nperturbations, the atom index, plus the coordinates and velocities\nfrom the molecules (if these could be found)\nThis is a monster function, as it does need to do everything, and\nthe parts of not easily decomposable (they need information from\na prior part that could be passed as function arguments, but\nwould be messy).\nThis function is best read as a sequence of stages. These stages\nare commented within the function. The stages are:\n1. Initialisation - copying molecular data from sire into OpenMMMolecule\n2. Create base forces (forcefields)\n3. Define the LambdaLever and LambdaSchedule\n4. Define the forces (forcefields) for the ghost atoms\n5. Copy atomistic forcefield parameters to the OpenMM forces\n6. Set up the nonbonded pair exceptions\n7. Set up the restraints\n8. Copy across all of the coordinates and velocities\n" ); + + } + +} diff --git a/wrapper/Convert/SireOpenMM/_SireOpenMM_free_functions.pypp.hpp b/wrapper/Convert/SireOpenMM/_SireOpenMM_free_functions.pypp.hpp new file mode 100644 index 000000000..39ba604b8 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/_SireOpenMM_free_functions.pypp.hpp @@ -0,0 +1,10 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#ifndef _SireOpenMM_free_functions_hpp__pyplusplus_wrapper +#define _SireOpenMM_free_functions_hpp__pyplusplus_wrapper + +void register_free_functions(); + +#endif//_SireOpenMM_free_functions_hpp__pyplusplus_wrapper diff --git a/wrapper/Convert/SireOpenMM/_perturbablemol.py b/wrapper/Convert/SireOpenMM/_perturbablemol.py new file mode 100644 index 000000000..b6b4da935 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/_perturbablemol.py @@ -0,0 +1,302 @@ +__all__ = [ + "_changed_atoms", + "_changed_bonds", + "_changed_angles", + "_changed_torsions", + "_changed_exceptions", + "_changed_constraints", +] + + +def _name(atom): + return f"{atom.name().value()}:{atom.number().value()}" + + +def _changed_atoms(obj, to_pandas: bool = True): + """ + Return a list of the atoms that change parameters in this + perturbation + + Parameters + ---------- + + to_pandas: bool, optional, default=True + If True then the list of atoms will be returned as a pandas + DataFrame + """ + changed_atoms = [] + + for atom, q0, s0, e0, a0, k0, q1, s1, e1, a1, k1 in zip( + obj.atoms(), + obj.get_charges0(), + obj.get_sigmas0(), + obj.get_epsilons0(), + obj.get_alphas0(), + obj.get_kappas0(), + obj.get_charges1(), + obj.get_sigmas1(), + obj.get_epsilons1(), + obj.get_alphas1(), + obj.get_kappas1(), + ): + if q0 != q1 or s0 != s1 or e0 != e1 or a0 != a1 or k0 != k1: + if abs(q0) <= 1e-9: + q0 = 0.0 + if abs(q1) <= 1e-9: + q1 = 0.0 + if abs(s0) <= 1e-9: + s0 = 0.0 + if abs(s1) <= 1e-9: + s1 = 0.0 + if abs(e0) <= 1e-9: + e0 = 0.0 + if abs(e1) <= 1e-9: + e1 = 0.0 + if abs(a0) <= 1e-9: + a0 = 0.0 + if abs(a1) <= 1e-9: + a1 = 0.0 + if abs(k0) <= 1e-9: + k0 = 0.0 + if abs(k1) <= 1e-9: + k1 = 0.0 + + if to_pandas: + atom = _name(atom) + + changed_atoms.append((atom, q0, q1, s0, s1, e0, e1, a0, a1, k0, k1)) + + if to_pandas: + import pandas as pd + + changed_atoms = pd.DataFrame( + changed_atoms, + columns=[ + "atom", + "charge0", + "charge1", + "sigma0", + "sigma1", + "epsilon0", + "epsilon1", + "alpha0", + "alpha1", + "kappa0", + "kappa1", + ], + ) + + return changed_atoms + + +def _changed_bonds(obj, to_pandas: bool = True): + """ + Return a list of the bonds that change parameters in this + perturbation + + Parameters + ---------- + + to_pandas: bool, optional, default=True + If True then the list of bonds will be returned as a pandas + DataFrame + """ + changed_bonds = [] + + for bond, r0, k0, r1, k1 in zip( + obj.bonds(), + obj.get_bond_lengths0(), + obj.get_bond_ks0(), + obj.get_bond_lengths1(), + obj.get_bond_ks1(), + ): + if r0 != r1 or k0 != k1: + if to_pandas: + bond = f"{_name(bond[0])}-{_name(bond[1])}" + + changed_bonds.append((bond, r0, r1, k0, k1)) + + if to_pandas: + import pandas as pd + + changed_bonds = pd.DataFrame( + changed_bonds, columns=["bond", "length0", "length1", "k0", "k1"] + ) + + return changed_bonds + + +def _changed_angles(obj, to_pandas: bool = True): + """ + Return a list of the angles that change parameters in this + perturbation + + Parameters + ---------- + + to_pandas: bool, optional, default=True + If True then the list of angles will be returned as a pandas + DataFrame + """ + changed_angles = [] + + for angle, theta0, k0, theta1, k1 in zip( + obj.angles(), + obj.get_angle_sizes0(), + obj.get_angle_ks0(), + obj.get_angle_sizes1(), + obj.get_angle_ks1(), + ): + if theta0 != theta1 or k0 != k1: + if to_pandas: + angle = f"{_name(angle[0])}-{_name(angle[1])}-{_name(angle[2])}" + + changed_angles.append((angle, theta0, theta1, k0, k1)) + + if to_pandas: + import pandas as pd + + changed_angles = pd.DataFrame( + changed_angles, columns=["angle", "size0", "size1", "k0", "k1"] + ) + + return changed_angles + + +def _changed_torsions(obj, to_pandas: bool = True): + """ + Return a list of the torsions that change parameters in this + perturbation. Note that this combines the dihedrals and improper + dihedrals into a single list. + + Parameters + ---------- + + to_pandas: bool, optional, default=True + If True then the list of torsions will be returned as a pandas + DataFrame + """ + changed_torsions = [] + + for torsion, k0, p0, ph0, k1, p1, ph1 in zip( + obj.torsions(), + obj.get_torsion_ks0(), + obj.get_torsion_periodicities0(), + obj.get_torsion_phases0(), + obj.get_torsion_ks1(), + obj.get_torsion_periodicities1(), + obj.get_torsion_phases1(), + ): + if k0 != k1 or p0 != p1 or ph0 != ph1: + if to_pandas: + torsion = f"{_name(torsion[0])}-{_name(torsion[1])}-{_name(torsion[2])}-{_name(torsion[3])}" + + changed_torsions.append((torsion, k0, k1, p0, p1, ph0, ph1)) + + if to_pandas: + import pandas as pd + + changed_torsions = pd.DataFrame( + changed_torsions, + columns=[ + "torsion", + "k0", + "k1", + "periodicity0", + "periodicity1", + "phase0", + "phase1", + ], + ) + + return changed_torsions + + +def _changed_exceptions(obj, to_pandas: bool = True): + """ + Return a list of the exceptions that change parameters in this + perturbation + + Parameters + ---------- + + to_pandas: bool, optional, default=True + If True then the list of exceptions will be returned as a pandas + DataFrame + """ + changed_exceptions = [] + + atoms = obj.atoms() + + for (atom0, atom1), q0, q1, lj0, lj1 in zip( + obj.get_exception_atoms(), + obj.get_charge_scales0(), + obj.get_charge_scales1(), + obj.get_lj_scales0(), + obj.get_lj_scales1(), + ): + if q0 != q1 or lj0 != lj1: + atom0 = atoms[atom0] + atom1 = atoms[atom1] + + atompair = (atom0, atom1) + + if to_pandas: + atompair = f"{_name(atom0)}-{_name(atom1)}" + + changed_exceptions.append((atompair, q0, q1, lj0, lj1)) + + if to_pandas: + import pandas as pd + + changed_exceptions = pd.DataFrame( + changed_exceptions, + columns=[ + "atompair", + "charge_scale0", + "charge_scale1", + "lj_scale0", + "lj_scale1", + ], + ) + + return changed_exceptions + + +def _changed_constraints(obj, to_pandas: bool = True): + """ + Return a list of the constraints that change parameters in this + perturbation + + Parameters + ---------- + + to_pandas: bool, optional, default=True + If True then the list of constraints will be returned as a pandas + DataFrame + """ + changed_constraints = [] + + atoms = obj.atoms() + + for atom0, atom1, r0, r1 in obj.get_perturbable_constraints_with_atoms(): + if r0 != r1: + atom0 = atoms[atom0] + atom1 = atoms[atom1] + + atompair = (atom0, atom1) + + if to_pandas: + atompair = f"{_name(atom0)}-{_name(atom1)}" + + changed_constraints.append((atompair, r0, r1)) + + if to_pandas: + import pandas as pd + + changed_constraints = pd.DataFrame( + changed_constraints, + columns=["atompair", "length0", "length1"], + ) + + return changed_constraints diff --git a/wrapper/Convert/SireOpenMM/_sommcontext.py b/wrapper/Convert/SireOpenMM/_sommcontext.py index 6af12732e..8f3c23d79 100644 --- a/wrapper/Convert/SireOpenMM/_sommcontext.py +++ b/wrapper/Convert/SireOpenMM/_sommcontext.py @@ -31,12 +31,12 @@ def __init__( """ if system is not None: from ...base import create_map - from ._SireOpenMM import _set_openmm_coordinates_and_velocities + from ._SireOpenMM import set_openmm_coordinates_and_velocities map = create_map(map) self._atom_index = metadata.index() - self._lambda_lever = metadata.lambdaLever() + self._lambda_lever = metadata.lambda_lever() # we need to update the constraints in the system # to match the current value of lambda, before we @@ -64,9 +64,11 @@ def __init__( super().__init__(system, integrator, platform) # place the coordinates and velocities into the context - _set_openmm_coordinates_and_velocities(self, metadata) + set_openmm_coordinates_and_velocities(self, metadata) - self._lambda_value = self._lambda_lever.set_lambda(self, lambda_value) + self._lambda_value = self._lambda_lever.set_lambda( + self, lambda_value=lambda_value, update_constraints=True + ) self._map = map else: @@ -175,9 +177,9 @@ def set_platform_property(self, key, value): f"are [ {keys} ]" ) - from ._SireOpenMM import _openmm_set_context_platform_property + from ._SireOpenMM import set_context_platform_property - _openmm_set_context_platform_property(self, key, value) + set_context_platform_property(self, key, value) def get_atom_index(self): """ @@ -207,7 +209,7 @@ def get_lambda_schedule(self): if self._lambda_lever is None: return None - return self._lambda_lever.schedule() + return self._lambda_lever.get_schedule() def set_lambda_schedule(self, schedule): """ @@ -219,15 +221,18 @@ def set_lambda_schedule(self, schedule): self._lambda_lever.set_schedule(schedule) - def set_lambda(self, lambda_value: float): + def set_lambda(self, lambda_value: float, update_constraints: bool = True): """ Update the parameters in the context to set the lambda value - to 'lamval' + to 'lamval'. If update_constraints is True then the constraints + will be updated to match the new value of lambda """ if self._lambda_lever is None: return - self._lambda_value = self._lambda_lever.set_lambda(self, lambda_value) + self._lambda_value = self._lambda_lever.set_lambda( + self, lambda_value=lambda_value, update_constraints=update_constraints + ) def set_temperature(self, temperature, rescale_velocities=True): """ @@ -263,3 +268,29 @@ def get_energy(self, to_sire_units: bool = True): Synonym for self.get_potential_energy() """ return self.get_potential_energy(to_sire_units=to_sire_units) + + def get_constraints(self): + """ + Return all pairs of atoms that are constrained, together with + the constraint distance + """ + s = self.getSystem() + + num_constraints = s.getNumConstraints() + + constraints = [] + + import openmm + from ...units import nanometer + + for i in range(num_constraints): + a1, a2, dist = s.getConstraintParameters(i) + + constraints.append( + ( + self._atom_index[a1] + self._atom_index[a2], + dist.value_in_unit(openmm.unit.nanometer) * nanometer, + ) + ) + + return constraints diff --git a/wrapper/Convert/SireOpenMM/active_headers.h b/wrapper/Convert/SireOpenMM/active_headers.h new file mode 100644 index 000000000..6803f33f1 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/active_headers.h @@ -0,0 +1,13 @@ +#ifndef ACTIVE_HEADERS_H +#define ACTIVE_HEADERS_H + +#ifdef GCCXML_PARSE + +#include "lambdalever.h" +#include "openmmminimise.h" +#include "openmmmolecule.h" +#include "sire_openmm.h" + +#endif + +#endif diff --git a/wrapper/Convert/SireOpenMM/lambdalever.cpp b/wrapper/Convert/SireOpenMM/lambdalever.cpp index 774749f72..204fa3b1f 100644 --- a/wrapper/Convert/SireOpenMM/lambdalever.cpp +++ b/wrapper/Convert/SireOpenMM/lambdalever.cpp @@ -43,7 +43,8 @@ MolLambdaCache::MolLambdaCache() : lam_val(0) { } -MolLambdaCache::MolLambdaCache(double lam) : lam_val(lam) +MolLambdaCache::MolLambdaCache(double lam) + : lam_val(lam) { } @@ -68,15 +69,18 @@ MolLambdaCache &MolLambdaCache::operator=(const MolLambdaCache &other) } const QVector &MolLambdaCache::morph(const LambdaSchedule &schedule, + const QString &force, const QString &key, const QVector &initial, const QVector &final) const { auto nonconst_this = const_cast(this); + QString force_key = force + "::" + key; + QReadLocker lkr(&(nonconst_this->lock)); - auto it = cache.constFind(key); + auto it = cache.constFind(force_key); if (it != cache.constEnd()) return it.value(); @@ -86,15 +90,41 @@ const QVector &MolLambdaCache::morph(const LambdaSchedule &schedule, QWriteLocker wkr(&(nonconst_this->lock)); // check that someone didn't beat us to create the values - it = cache.constFind(key); + it = cache.constFind(force_key); if (it != cache.constEnd()) return it.value(); - // create the values - nonconst_this->cache.insert(key, schedule.morph(key, initial, final, lam_val)); + const auto stage = schedule.getStage(lam_val); - return cache.constFind(key).value(); + if (schedule.hasForceSpecificEquation(stage, force, key)) + { + // create the values specific for this lever for this force + nonconst_this->cache.insert(force_key, + schedule.morph(force, key, + initial, final, lam_val)); + } + else + { + // all forces use the same equation for this lever + // Look for the common equation + it = cache.constFind(key); + + if (it == cache.constEnd()) + { + // we're the first - create the values and cache them + nonconst_this->cache.insert(key, + schedule.morph("*", key, + initial, final, lam_val)); + + it = cache.constFind(key); + } + + // save this equation for this force for this lever + nonconst_this->cache.insert(force_key, it.value()); + } + + return cache.constFind(force_key).value(); } ////// @@ -274,18 +304,20 @@ QString LambdaLever::getForceType(const QString &name, return QString::fromStdString(force.getName()); } -std::tuple +boost::tuple get_exception(int atom0, int atom1, int start_index, double coul_14_scl, double lj_14_scl, const QVector &morphed_charges, const QVector &morphed_sigmas, const QVector &morphed_epsilons, - const QVector &morphed_alphas) + const QVector &morphed_alphas, + const QVector &morphed_kappas) { double charge = 0.0; double sigma = 0.0; double epsilon = 0.0; double alpha = 0.0; + double kappa = 0.0; if (coul_14_scl != 0 or lj_14_scl != 0) { @@ -325,6 +357,17 @@ get_exception(int atom0, int atom1, int start_index, alpha = 0; else if (alpha > 1) alpha = 1; + + if (not morphed_kappas.isEmpty()) + { + kappa = std::max(morphed_kappas[atom0], morphed_kappas[atom1]); + } + + // clamp kappa between 0 and 1 + if (kappa < 0) + kappa = 0; + else if (kappa > 1) + kappa = 1; } if (charge == 0 and epsilon == 0) @@ -338,17 +381,18 @@ get_exception(int atom0, int atom1, int start_index, epsilon = 1e-9; } - return std::make_tuple(atom0 + start_index, - atom1 + start_index, - charge, sigma, epsilon, - alpha); + return boost::make_tuple(atom0 + start_index, + atom1 + start_index, + charge, sigma, epsilon, + alpha, kappa); } /** Set the value of lambda in the passed context. Returns the * actual value of lambda set. */ double LambdaLever::setLambda(OpenMM::Context &context, - double lambda_value) const + double lambda_value, + bool update_constraints) const { // go over each forcefield and update the parameters in the forcefield, // and then pass those updated parameters to the context @@ -373,7 +417,7 @@ double LambdaLever::setLambda(OpenMM::Context &context, // we know if we have peturbable ghost atoms if we have the ghost forcefields const bool have_ghost_atoms = (ghost_ghostff != 0 or ghost_nonghostff != 0); - std::vector custom_params = {0.0, 0.0, 0.0, 0.0}; + std::vector custom_params = {0.0, 0.0, 0.0, 0.0, 0.0}; // record the range of indicies of the atoms, bonds, angles, // torsions which change @@ -395,85 +439,157 @@ double LambdaLever::setLambda(OpenMM::Context &context, const auto &start_idxs = this->start_indicies[i]; const auto &cache = this->lambda_cache.get(i, lambda_value); - - // calculate the new parameters for this lambda value - const auto morphed_charges = cache.morph( - this->lambda_schedule, - "charge", - perturbable_mol.getCharges0(), - perturbable_mol.getCharges1()); - - const auto morphed_sigmas = cache.morph( - this->lambda_schedule, - "sigma", - perturbable_mol.getSigmas0(), - perturbable_mol.getSigmas1()); - - const auto morphed_epsilons = cache.morph( - this->lambda_schedule, - "epsilon", - perturbable_mol.getEpsilons0(), - perturbable_mol.getEpsilons1()); - - const auto morphed_alphas = cache.morph( - this->lambda_schedule, - "alpha", - perturbable_mol.getAlphas0(), - perturbable_mol.getAlphas1()); - - const auto morphed_bond_k = cache.morph( - this->lambda_schedule, - "bond_k", - perturbable_mol.getBondKs0(), - perturbable_mol.getBondKs1()); - - const auto morphed_bond_length = cache.morph( - this->lambda_schedule, - "bond_length", - perturbable_mol.getBondLengths0(), - perturbable_mol.getBondLengths1()); - - const auto morphed_angle_k = cache.morph( - this->lambda_schedule, - "angle_k", - perturbable_mol.getAngleKs0(), - perturbable_mol.getAngleKs1()); - - const auto morphed_angle_size = cache.morph( - this->lambda_schedule, - "angle_size", - perturbable_mol.getAngleSizes0(), - perturbable_mol.getAngleSizes1()); - - const auto morphed_torsion_phase = cache.morph( - this->lambda_schedule, - "torsion_phase", - perturbable_mol.getTorsionPhases0(), - perturbable_mol.getTorsionPhases1()); - - const auto morphed_torsion_k = cache.morph( - this->lambda_schedule, - "torsion_k", - perturbable_mol.getTorsionKs0(), - perturbable_mol.getTorsionKs1()); - - const auto morphed_charge_scale = cache.morph( - this->lambda_schedule, - "charge_scale", - perturbable_mol.getChargeScales0(), - perturbable_mol.getChargeScales1()); - - const auto morphed_lj_scale = cache.morph( - this->lambda_schedule, - "lj_scale", - perturbable_mol.getLJScales0(), - perturbable_mol.getLJScales1()); + const auto &schedule = this->lambda_schedule.getMoleculeSchedule(i); // now update the forcefields int start_index = start_idxs.value("clj", -1); if (start_index != -1 and cljff != 0) { + const auto morphed_charges = cache.morph( + schedule, + "clj", "charge", + perturbable_mol.getCharges0(), + perturbable_mol.getCharges1()); + + const auto morphed_sigmas = cache.morph( + schedule, + "clj", "sigma", + perturbable_mol.getSigmas0(), + perturbable_mol.getSigmas1()); + + const auto morphed_epsilons = cache.morph( + schedule, + "clj", "epsilon", + perturbable_mol.getEpsilons0(), + perturbable_mol.getEpsilons1()); + + const auto morphed_alphas = cache.morph( + schedule, + "clj", "alpha", + perturbable_mol.getAlphas0(), + perturbable_mol.getAlphas1()); + + const auto morphed_kappas = cache.morph( + schedule, + "clj", "kappa", + perturbable_mol.getKappas0(), + perturbable_mol.getKappas1()); + + const auto morphed_charge_scale = cache.morph( + schedule, + "clj", "charge_scale", + perturbable_mol.getChargeScales0(), + perturbable_mol.getChargeScales1()); + + const auto morphed_lj_scale = cache.morph( + schedule, + "clj", "lj_scale", + perturbable_mol.getLJScales0(), + perturbable_mol.getLJScales1()); + + const auto morphed_ghost_charges = cache.morph( + schedule, + "ghost/ghost", "charge", + perturbable_mol.getCharges0(), + perturbable_mol.getCharges1()); + + const auto morphed_ghost_sigmas = cache.morph( + schedule, + "ghost/ghost", "sigma", + perturbable_mol.getSigmas0(), + perturbable_mol.getSigmas1()); + + const auto morphed_ghost_epsilons = cache.morph( + schedule, + "ghost/ghost", "epsilon", + perturbable_mol.getEpsilons0(), + perturbable_mol.getEpsilons1()); + + const auto morphed_ghost_alphas = cache.morph( + schedule, + "ghost/ghost", "alpha", + perturbable_mol.getAlphas0(), + perturbable_mol.getAlphas1()); + + const auto morphed_ghost_kappas = cache.morph( + schedule, + "ghost/ghost", "kappa", + perturbable_mol.getKappas0(), + perturbable_mol.getKappas1()); + + const auto morphed_nonghost_charges = cache.morph( + schedule, + "ghost/non-ghost", "charge", + perturbable_mol.getCharges0(), + perturbable_mol.getCharges1()); + + const auto morphed_nonghost_sigmas = cache.morph( + schedule, + "ghost/non-ghost", "sigma", + perturbable_mol.getSigmas0(), + perturbable_mol.getSigmas1()); + + const auto morphed_nonghost_epsilons = cache.morph( + schedule, + "ghost/non-ghost", "epsilon", + perturbable_mol.getEpsilons0(), + perturbable_mol.getEpsilons1()); + + const auto morphed_nonghost_alphas = cache.morph( + schedule, + "ghost/non-ghost", "alpha", + perturbable_mol.getAlphas0(), + perturbable_mol.getAlphas1()); + + const auto morphed_nonghost_kappas = cache.morph( + schedule, + "ghost/non-ghost", "kappa", + perturbable_mol.getKappas0(), + perturbable_mol.getKappas1()); + + const auto morphed_ghost14_charges = cache.morph( + schedule, + "ghost-14", "charge", + perturbable_mol.getCharges0(), + perturbable_mol.getCharges1()); + + const auto morphed_ghost14_sigmas = cache.morph( + schedule, + "ghost-14", "sigma", + perturbable_mol.getSigmas0(), + perturbable_mol.getSigmas1()); + + const auto morphed_ghost14_epsilons = cache.morph( + schedule, + "ghost-14", "epsilon", + perturbable_mol.getEpsilons0(), + perturbable_mol.getEpsilons1()); + + const auto morphed_ghost14_alphas = cache.morph( + schedule, + "ghost-14", "alpha", + perturbable_mol.getAlphas0(), + perturbable_mol.getAlphas1()); + + const auto morphed_ghost14_kappas = cache.morph( + schedule, + "ghost-14", "kappa", + perturbable_mol.getKappas0(), + perturbable_mol.getKappas1()); + + const auto morphed_ghost14_charge_scale = cache.morph( + schedule, + "ghost-14", "charge_scale", + perturbable_mol.getChargeScales0(), + perturbable_mol.getChargeScales1()); + + const auto morphed_ghost14_lj_scale = cache.morph( + schedule, + "ghost-14", "lj_scale", + perturbable_mol.getLJScales0(), + perturbable_mol.getLJScales1()); + const int nparams = morphed_charges.count(); if (start_change_atom == -1) @@ -494,13 +610,15 @@ double LambdaLever::setLambda(OpenMM::Context &context, const bool is_to_ghost = perturbable_mol.getToGhostIdxs().contains(j); // reduced charge - custom_params[0] = morphed_charges[j]; + custom_params[0] = morphed_ghost_charges[j]; // half_sigma - custom_params[1] = 0.5 * morphed_sigmas[j]; + custom_params[1] = 0.5 * morphed_ghost_sigmas[j]; // two_sqrt_epsilon - custom_params[2] = 2.0 * std::sqrt(morphed_epsilons[j]); + custom_params[2] = 2.0 * std::sqrt(morphed_ghost_epsilons[j]); // alpha - custom_params[3] = morphed_alphas[j]; + custom_params[3] = morphed_ghost_alphas[j]; + // kappa + custom_params[4] = morphed_ghost_kappas[j]; // clamp alpha between 0 and 1 if (custom_params[3] < 0) @@ -508,7 +626,37 @@ double LambdaLever::setLambda(OpenMM::Context &context, else if (custom_params[3] > 1) custom_params[3] = 1; + // clamp kappa between 0 and 1 + if (custom_params[4] < 0) + custom_params[4] = 0; + else if (custom_params[4] > 1) + custom_params[4] = 1; + ghost_ghostff->setParticleParameters(start_index + j, custom_params); + + // reduced charge + custom_params[0] = morphed_nonghost_charges[j]; + // half_sigma + custom_params[1] = 0.5 * morphed_nonghost_sigmas[j]; + // two_sqrt_epsilon + custom_params[2] = 2.0 * std::sqrt(morphed_nonghost_epsilons[j]); + // alpha + custom_params[3] = morphed_nonghost_alphas[j]; + // kappa + custom_params[4] = morphed_nonghost_kappas[j]; + + // clamp alpha between 0 and 1 + if (custom_params[3] < 0) + custom_params[3] = 0; + else if (custom_params[3] > 1) + custom_params[3] = 1; + + // clamp kappa between 0 and 1 + if (custom_params[4] < 0) + custom_params[4] = 0; + else if (custom_params[4] > 1) + custom_params[4] = 1; + ghost_nonghostff->setParticleParameters(start_index + j, custom_params); if (is_from_ghost or is_to_ghost) @@ -540,74 +688,118 @@ double LambdaLever::setLambda(OpenMM::Context &context, { const auto &atoms = exception_atoms[j]; - const auto atom0 = std::get<0>(atoms); - const auto atom1 = std::get<1>(atoms); + const auto atom0 = boost::get<0>(atoms); + const auto atom1 = boost::get<1>(atoms); - const auto coul_14_scale = morphed_charge_scale[j]; - const auto lj_14_scale = morphed_lj_scale[j]; + auto coul_14_scale = morphed_charge_scale[j]; + auto lj_14_scale = morphed_lj_scale[j]; const bool atom0_is_ghost = perturbable_mol.isGhostAtom(atom0); const bool atom1_is_ghost = perturbable_mol.isGhostAtom(atom1); - const auto p = get_exception(atom0, atom1, - start_index, coul_14_scale, lj_14_scale, - morphed_charges, morphed_sigmas, morphed_epsilons, - morphed_alphas); + auto p = get_exception(atom0, atom1, + start_index, coul_14_scale, lj_14_scale, + morphed_charges, morphed_sigmas, morphed_epsilons, + morphed_alphas, morphed_kappas); // don't set LJ terms for ghost atoms if (atom0_is_ghost or atom1_is_ghost) { cljff->setExceptionParameters( - std::get<0>(idxs[j]), - std::get<0>(p), std::get<1>(p), - std::get<2>(p), 1e-9, 1e-9); + boost::get<0>(idxs[j]), + boost::get<0>(p), boost::get<1>(p), + boost::get<2>(p), 1e-9, 1e-9); - if (coul_14_scale != 0 or lj_14_scale != 0) + if (ghost_14ff != 0) { // this is a 1-4 parameter - need to update // the ghost 1-4 forcefield - int nbidx = std::get<1>(idxs[j]); + int nbidx = boost::get<1>(idxs[j]); if (nbidx < 0) throw SireError::program_bug(QObject::tr( "Unset NB14 index for a ghost atom?"), CODELOC); - if (ghost_14ff != 0) - { - // parameters are q, sigma, four_epsilon and alpha - std::vector params14 = - {std::get<2>(p), std::get<3>(p), - 4.0 * std::get<4>(p), std::get<5>(p)}; + coul_14_scale = morphed_ghost14_charge_scale[j]; + lj_14_scale = morphed_ghost14_lj_scale[j]; + + const auto p = get_exception(atom0, atom1, + start_index, coul_14_scale, lj_14_scale, + morphed_ghost14_charges, + morphed_ghost14_sigmas, + morphed_ghost14_epsilons, + morphed_ghost14_alphas, + morphed_ghost14_kappas); - if (start_change_14 == -1) - { + // parameters are q, sigma, four_epsilon and alpha + std::vector params14 = + {boost::get<2>(p), boost::get<3>(p), + 4.0 * boost::get<4>(p), boost::get<5>(p), + boost::get<6>(p)}; + + if (start_change_14 == -1) + { + start_change_14 = nbidx; + end_change_14 = nbidx + 1; + } + else + { + if (nbidx < start_change_14) start_change_14 = nbidx; + + if (nbidx + 1 > end_change_14) end_change_14 = nbidx + 1; - } - else - { - if (nbidx < start_change_14) - start_change_14 = nbidx; - - if (nbidx + 1 > end_change_14) - end_change_14 = nbidx + 1; - } - - ghost_14ff->setBondParameters(nbidx, - std::get<0>(p), - std::get<1>(p), - params14); } + + ghost_14ff->setBondParameters(nbidx, + boost::get<0>(p), + boost::get<1>(p), + params14); } } else { cljff->setExceptionParameters( - std::get<0>(idxs[j]), - std::get<0>(p), std::get<1>(p), - std::get<2>(p), std::get<3>(p), - std::get<4>(p)); + boost::get<0>(idxs[j]), + boost::get<0>(p), boost::get<1>(p), + boost::get<2>(p), boost::get<3>(p), + boost::get<4>(p)); + } + } + } + } + + // update all of the perturbable constraints + if (update_constraints) + { + auto perturbable_constraints = perturbable_mol.getPerturbableConstraints(); + + const auto &idxs = boost::get<0>(perturbable_constraints); + const auto &r0_0 = boost::get<1>(perturbable_constraints); + const auto &r0_1 = boost::get<2>(perturbable_constraints); + + if (not idxs.isEmpty()) + { + const auto morphed_constraint_length = cache.morph( + schedule, + "bond", "bond_length", + r0_0, r0_1); + + for (int j = 0; j < idxs.count(); ++j) + { + const auto idx = idxs[j]; + + const auto constraint_length = morphed_constraint_length[j]; + + int particle1, particle2; + double orig_distance; + + system.getConstraintParameters(idx, particle1, particle2, orig_distance); + + if (orig_distance != constraint_length) + { + system.setConstraintParameters(idx, particle1, particle2, constraint_length); } } } @@ -617,6 +809,18 @@ double LambdaLever::setLambda(OpenMM::Context &context, if (start_index != -1 and bondff != 0) { + const auto morphed_bond_k = cache.morph( + schedule, + "bond", "bond_k", + perturbable_mol.getBondKs0(), + perturbable_mol.getBondKs1()); + + const auto morphed_bond_length = cache.morph( + schedule, + "bond", "bond_length", + perturbable_mol.getBondLengths0(), + perturbable_mol.getBondLengths1()); + const int nparams = morphed_bond_k.count(); if (start_change_bond == -1) @@ -654,6 +858,18 @@ double LambdaLever::setLambda(OpenMM::Context &context, if (start_index != -1 and angff != 0) { + const auto morphed_angle_k = cache.morph( + schedule, + "angle", "angle_k", + perturbable_mol.getAngleKs0(), + perturbable_mol.getAngleKs1()); + + const auto morphed_angle_size = cache.morph( + schedule, + "angle", "angle_size", + perturbable_mol.getAngleSizes0(), + perturbable_mol.getAngleSizes1()); + const int nparams = morphed_angle_k.count(); if (start_change_angle == -1) @@ -693,6 +909,18 @@ double LambdaLever::setLambda(OpenMM::Context &context, if (start_index != -1 and dihff != 0) { + const auto morphed_torsion_phase = cache.morph( + schedule, + "torsion", "torsion_phase", + perturbable_mol.getTorsionPhases0(), + perturbable_mol.getTorsionPhases1()); + + const auto morphed_torsion_k = cache.morph( + schedule, + "torsion", "torsion_k", + perturbable_mol.getTorsionKs0(), + perturbable_mol.getTorsionKs1()); + const int nparams = morphed_torsion_k.count(); if (start_change_torsion == -1) @@ -798,7 +1026,9 @@ double LambdaLever::setLambda(OpenMM::Context &context, // restraints always morph between 1 and 1 (i.e. they fully // follow whatever is set by lambda, e.g. 'initial*lambda' // to switch them on, or `final*lambda` to switch them off) - const double rho = lambda_schedule.morph(restraint, + const double rho = lambda_schedule.morph("*", + + restraint, 1.0, 1.0, lambda_value); @@ -842,7 +1072,7 @@ void _update_restraint_in_context(OpenMM::CustomCompoundBondForce *ff, double rh // is from the first restraint. This is because rho should be the // first parameter and have the same value for all restraints std::vector custom_params; - std::vector particles; + std::vector particles; custom_params.resize(nparams); particles.resize(ff->getNumParticlesPerBond()); @@ -956,6 +1186,7 @@ void LambdaLever::setForceIndex(const QString &force, CODELOC); this->name_to_ffidx.insert(force, index); + this->lambda_schedule.addForce(force); } /** Add the index of a restraint force called 'restraint' in the @@ -1016,13 +1247,23 @@ int LambdaLever::addPerturbableMolecule(const OpenMMMolecule &molecule, */ void LambdaLever::setExceptionIndicies(int mol_idx, const QString &name, - const QVector> &exception_idxs) + const QVector> &exception_idxs) { mol_idx = SireID::Index(mol_idx).map(this->perturbable_mols.count()); this->perturbable_mols[mol_idx].setExceptionIndicies(name, exception_idxs); } +/** Set the constraint indicies for the perturbable molecule at + * index 'mol_idx' + */ +void LambdaLever::setConstraintIndicies(int mol_idx, const QVector &constraint_idxs) +{ + mol_idx = SireID::Index(mol_idx).map(this->perturbable_mols.count()); + + this->perturbable_mols[mol_idx].setConstraintIndicies(constraint_idxs); +} + /** Return all of the property maps used to find the perturbable properties * of the perturbable molecules. This is indexed by molecule number */ diff --git a/wrapper/Convert/SireOpenMM/lambdalever.h b/wrapper/Convert/SireOpenMM/lambdalever.h index f934c7d4a..8521972d1 100644 --- a/wrapper/Convert/SireOpenMM/lambdalever.h +++ b/wrapper/Convert/SireOpenMM/lambdalever.h @@ -35,6 +35,8 @@ #include +#include + #include SIRE_BEGIN_HEADER @@ -52,7 +54,7 @@ namespace SireOpenMM MolLambdaCache &operator=(const MolLambdaCache &other); const QVector &morph(const SireCAS::LambdaSchedule &schedule, - const QString &key, + const QString &force, const QString &key, const QVector &initial, const QVector &final) const; @@ -104,7 +106,8 @@ namespace SireOpenMM const char *what() const; static const char *typeName(); - double setLambda(OpenMM::Context &context, double lam_val) const; + double setLambda(OpenMM::Context &system, double lambda_value, + bool update_constraints = true) const; void setForceIndex(const QString &force, int index); @@ -114,7 +117,9 @@ namespace SireOpenMM const QHash &start_indicies); void setExceptionIndicies(int idx, const QString &ff, - const QVector> &exception_idxs); + const QVector> &exception_idxs); + + void setConstraintIndicies(int idx, const QVector &constraint_idxs); void setSchedule(const SireCAS::LambdaSchedule &schedule); @@ -141,11 +146,11 @@ namespace SireOpenMM OpenMM::Context &context) const; /** Map from a forcefield name to its index in the associated System */ - QHash name_to_ffidx; + QHash name_to_ffidx; /** Map from a restraint name to its index in the associated System. * Note that multiple restraints can have the same name */ - QMultiHash name_to_restraintidx; + QMultiHash name_to_restraintidx; /** The schedule used to set lambda */ SireCAS::LambdaSchedule lambda_schedule; @@ -184,6 +189,14 @@ namespace SireOpenMM return "OpenMM::CustomBondForce"; } + // LESTER - UNCOMMENT BELOW FOR FEATURE_EMLE + + /*template <> + inline QString _get_typename() + { + return "SireOpenMM::QMMMForce"; + }*/ + /** Return the OpenMM::Force (of type T) that is called 'name' * from the passed OpenMM::System. This returns 0 if the force * doesn't exist @@ -235,6 +248,8 @@ namespace SireOpenMM Q_DECLARE_METATYPE(SireOpenMM::LambdaLever) +SIRE_EXPOSE_CLASS(SireOpenMM::LambdaLever) + SIRE_END_HEADER #endif diff --git a/wrapper/Convert/SireOpenMM/openmmminimise.h b/wrapper/Convert/SireOpenMM/openmmminimise.h index 03180c182..8ab26c233 100644 --- a/wrapper/Convert/SireOpenMM/openmmminimise.h +++ b/wrapper/Convert/SireOpenMM/openmmminimise.h @@ -25,6 +25,8 @@ namespace SireOpenMM } +SIRE_EXPOSE_FUNCTION(SireOpenMM::minimise_openmm_context) + SIRE_END_HEADER #endif diff --git a/wrapper/Convert/SireOpenMM/openmmmolecule.cpp b/wrapper/Convert/SireOpenMM/openmmmolecule.cpp index 9655c73cf..694e7fb99 100644 --- a/wrapper/Convert/SireOpenMM/openmmmolecule.cpp +++ b/wrapper/Convert/SireOpenMM/openmmmolecule.cpp @@ -33,6 +33,8 @@ #include +#include + #include using namespace SireOpenMM; @@ -85,34 +87,70 @@ OpenMMMolecule::OpenMMMolecule(const Molecule &mol, if (map.specified("constraint")) { - const auto c = map["constraint"].source().toLower().simplified(); + const auto c = map["constraint"].source().toLower().simplified().replace("_", "-"); if (c == "none") { constraint_type = CONSTRAIN_NONE; } - else if (c == "h-bonds" or c == "h_bonds") + else if (c == "h-bonds") { constraint_type = CONSTRAIN_HBONDS; } + else if (c == "h-bonds-not-perturbed") + { + constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_NOT_PERTURBED; + } + else if (c == "h-bonds-not-heavy-perturbed") + { + constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_NOT_HEAVY_PERTURBED; + } + else if (c == "h-bonds-h-angles") + { + constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_HANGLES; + } + else if (c == "h-bonds-h-angles-not-perturbed") + { + constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_HANGLES | CONSTRAIN_NOT_PERTURBED; + } + else if (c == "h-bonds-h-angles-not-heavy-perturbed") + { + constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_HANGLES | CONSTRAIN_NOT_HEAVY_PERTURBED; + } else if (c == "bonds") { constraint_type = CONSTRAIN_BONDS; } - else if (c == "h-bonds-h-angles" or c == "h_bonds_h_angles") + else if (c == "bonds-not-perturbed") { - constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_HANGLES; + constraint_type = CONSTRAIN_BONDS | CONSTRAIN_NOT_PERTURBED; + } + else if (c == "bonds-not-heavy-perturbed") + { + constraint_type = CONSTRAIN_BONDS | CONSTRAIN_NOT_HEAVY_PERTURBED; } - else if (c == "bonds-h-angles" or c == "bonds_h_angles") + else if (c == "bonds-h-angles") { constraint_type = CONSTRAIN_BONDS | CONSTRAIN_HANGLES; } + else if (c == "bonds-h-angles-not-perturbed") + { + constraint_type = CONSTRAIN_BONDS | CONSTRAIN_HANGLES | CONSTRAIN_NOT_PERTURBED; + } + else if (c == "bonds-h-angles-not-heavy-perturbed") + { + constraint_type = CONSTRAIN_BONDS | CONSTRAIN_HANGLES | CONSTRAIN_NOT_HEAVY_PERTURBED; + } else { throw SireError::invalid_key(QObject::tr( "Unrecognised constraint type '%1'. Valid values are " - "'none', 'h-bonds', 'bonds', 'h-bonds-h-angles' or " - "'bonds-h-angles',") + "'none', 'h-bonds', " + "'h-bonds-not-perturbed', 'h-bonds-not-heavy-perturbed', " + "'h-bonds-h-angles-not-perturbed', 'h-bonds-h-angles-not-heavy-perturbed' " + "'bonds', 'bonds-not-perturbed', 'bonds-not-heavy-perturbed', " + "'bonds-h-angles', 'bonds-h-angles-not-perturbed' or " + "'bonds-h-angles-not-heavy-perturbed'.") .arg(c), CODELOC); } @@ -124,34 +162,70 @@ OpenMMMolecule::OpenMMMolecule(const Molecule &mol, if (map.specified("perturbable_constraint")) { - const auto c = map["perturbable_constraint"].source().toLower().simplified(); + const auto c = map["perturbable_constraint"].source().toLower().simplified().replace("_", "-"); if (c == "none") { perturbable_constraint_type = CONSTRAIN_NONE; } - else if (c == "h-bonds" or c == "h_bonds") + else if (c == "h-bonds") { perturbable_constraint_type = CONSTRAIN_HBONDS; } + else if (c == "h-bonds-not-perturbed") + { + perturbable_constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_NOT_PERTURBED; + } + else if (c == "h-bonds-not-heavy-perturbed") + { + perturbable_constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_NOT_HEAVY_PERTURBED; + } + else if (c == "h-bonds-h-angles") + { + perturbable_constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_HANGLES; + } + else if (c == "h-bonds-h-angles-not-perturbed") + { + perturbable_constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_HANGLES | CONSTRAIN_NOT_PERTURBED; + } + else if (c == "h-bonds-h-angles-not-heavy-perturbed") + { + perturbable_constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_HANGLES | CONSTRAIN_NOT_HEAVY_PERTURBED; + } else if (c == "bonds") { perturbable_constraint_type = CONSTRAIN_BONDS; } - else if (c == "h-bonds-h-angles" or c == "h_bonds_h_angles") + else if (c == "bonds-not-perturbed") { - perturbable_constraint_type = CONSTRAIN_HBONDS | CONSTRAIN_HANGLES; + perturbable_constraint_type = CONSTRAIN_BONDS | CONSTRAIN_NOT_PERTURBED; + } + else if (c == "bonds-not-heavy-perturbed") + { + perturbable_constraint_type = CONSTRAIN_BONDS | CONSTRAIN_NOT_HEAVY_PERTURBED; } - else if (c == "bonds-h-angles" or c == "bonds_h_angles") + else if (c == "bonds-h-angles") { perturbable_constraint_type = CONSTRAIN_BONDS | CONSTRAIN_HANGLES; } + else if (c == "bonds-h-angles-not-perturbed") + { + perturbable_constraint_type = CONSTRAIN_BONDS | CONSTRAIN_HANGLES | CONSTRAIN_NOT_PERTURBED; + } + else if (c == "bonds-h-angles-not-heavy-perturbed") + { + perturbable_constraint_type = CONSTRAIN_BONDS | CONSTRAIN_HANGLES | CONSTRAIN_NOT_HEAVY_PERTURBED; + } else { throw SireError::invalid_key(QObject::tr( "Unrecognised perturbable constraint type '%1'. Valid values are " - "'none', 'h-bonds', 'bonds', 'h-bonds-h-angles' or " - "'bonds-h-angles',") + "'none', 'h-bonds', " + "'h-bonds-not-perturbed', 'h-bonds-not-heavy-perturbed', " + "'h-bonds-h-angles-not-perturbed', 'h-bonds-h-angles-not-heavy-perturbed' " + "'bonds', 'bonds-not-perturbed', 'bonds-not-heavy-perturbed', " + "'bonds-h-angles', 'bonds-h-angles-not-perturbed' or " + "'bonds-h-angles-not-heavy-perturbed'.") .arg(c), CODELOC); } @@ -192,18 +266,34 @@ OpenMMMolecule::OpenMMMolecule(const Molecule &mol, // updating coordinates after minimisation perturtable_map = map0; + bool ignore_perturbations = false; + + if (map.specified("ignore_perturbations")) + { + ignore_perturbations = map["ignore_perturbations"].value().asABoolean(); + } + // extract the parameters in amber format - this should work, // as the 'forcefield' property has promised that this is // an amber-style molecule const auto params = SireMM::AmberParams(mol, map0); - const auto params1 = SireMM::AmberParams(mol, map1); - perturbed.reset(new OpenMMMolecule(*this)); - perturbed->constructFromAmber(mol, params1, params, map1, true); + if (ignore_perturbations) + { + const auto params = SireMM::AmberParams(mol, map0); + this->constructFromAmber(mol, params, params, map0, false); + } + else + { + const auto params1 = SireMM::AmberParams(mol, map1); + + perturbed.reset(new OpenMMMolecule(*this)); + perturbed->constructFromAmber(mol, params1, params, map1, true); - this->constructFromAmber(mol, params, params1, map0, true); + this->constructFromAmber(mol, params, params1, map0, true); - this->alignInternals(map); + this->alignInternals(map); + } } else { @@ -272,7 +362,7 @@ inline qint64 to_pair(qint64 x, qint64 y) return x << 32 | y & 0x00000000FFFFFFFF; } -std::tuple OpenMMMolecule::getException( +boost::tuple OpenMMMolecule::getException( int atom0, int atom1, int start_index, double coul_14_scl, double lj_14_scl) const { double charge = 0.0; @@ -291,9 +381,9 @@ std::tuple OpenMMMolecule::getException( const auto &clj0 = cljs.constData()[atom0]; const auto &clj1 = cljs.constData()[atom1]; - charge = coul_14_scl * std::get<0>(clj0) * std::get<0>(clj1); - sigma = 0.5 * (std::get<1>(clj0) + std::get<1>(clj1)); - epsilon = lj_14_scl * std::sqrt(std::get<2>(clj0) * std::get<2>(clj1)); + charge = coul_14_scl * boost::get<0>(clj0) * boost::get<0>(clj1); + sigma = 0.5 * (boost::get<1>(clj0) + boost::get<1>(clj1)); + epsilon = lj_14_scl * std::sqrt(boost::get<2>(clj0) * boost::get<2>(clj1)); } if (this->isPerturbable() and charge == 0 and std::abs(epsilon) < 1e-9) @@ -313,9 +403,9 @@ std::tuple OpenMMMolecule::getException( epsilon = 0; } - return std::make_tuple(atom0 + start_index, - atom1 + start_index, - charge, sigma, epsilon); + return boost::make_tuple(atom0 + start_index, + atom1 + start_index, + charge, sigma, epsilon); } /** Return closest constraint length to 'length' based on what @@ -430,30 +520,75 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, auto masses_data = masses.data(); + const auto &elements = params.elements(); + const auto &ambertypes = params.amberTypes(); + + bool check_for_h_by_mass = true; + + if (map.specified("check_for_h_by_mass")) + { + check_for_h_by_mass = map["check_for_h_by_mass"].value().asABoolean(); + } + + bool check_for_h_by_element = true; + + if (map.specified("check_for_h_by_element")) + { + check_for_h_by_element = map["check_for_h_by_element"].value().asABoolean(); + } + + bool check_for_h_by_ambertype = true; + + if (map.specified("check_for_h_by_ambertype")) + { + check_for_h_by_ambertype = map["check_for_h_by_ambertype"].value().asABoolean(); + } + if (is_perturbable) { const auto params1_masses = params1.masses(); + const auto &elements1 = params1.elements(); + const auto &ambertypes1 = params1.amberTypes(); + for (int i = 0; i < nats; ++i) { const auto cgatomidx = idx_to_cgatomidx_data[i]; // use the largest mass of the perturbing atoms - double mass = std::max(params_masses.at(cgatomidx).to(SireUnits::g_per_mol), - params1_masses.at(cgatomidx).to(SireUnits::g_per_mol)); + const double mass0 = params_masses.at(cgatomidx).to(SireUnits::g_per_mol); + const double mass1 = params1_masses.at(cgatomidx).to(SireUnits::g_per_mol); + + double mass = std::max(mass0, mass1); if (mass < 0.05) { mass = 0.0; } - if (mass < 0.5) + if (mass < 1) + { + // this must be a ghost in both end states? + light_atoms.insert(i); + } + else if (check_for_h_by_mass and (mass < 2.5 or mass1 < 2.5)) { - virtual_sites.append(i); + // one of the atoms is H or He + light_atoms.insert(i); } - else if (mass < 2.5) + else if (check_for_h_by_element and + (elements.at(cgatomidx).nProtons() == 1 or + elements1.at(cgatomidx).nProtons() == 1)) { - light_atoms.append(i); + // one of the atoms is H + light_atoms.insert(i); + } + else if (check_for_h_by_ambertype and + (ambertypes.at(cgatomidx).toLower().startsWith("h") or + ambertypes1.at(cgatomidx).toLower().startsWith("h"))) + { + // one of the atoms has a H? amber type + light_atoms.insert(i); } masses_data[i] = mass; @@ -470,13 +605,22 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, mass = 0.0; } - if (mass < 0.5) + if (mass < 1) + { + // this must be a ghost + light_atoms.insert(i); + } + else if (check_for_h_by_mass and mass < 2.5) { - virtual_sites.append(i); + light_atoms.insert(i); } - else if (mass < 2.5) + else if (check_for_h_by_element and elements.at(idx_to_cgatomidx_data[i]).nProtons() == 1) { - light_atoms.append(i); + light_atoms.insert(i); + } + else if (check_for_h_by_ambertype and ambertypes.at(idx_to_cgatomidx_data[i]).toLower().startsWith("h")) + { + light_atoms.insert(i); } masses_data[i] = mass; @@ -487,7 +631,7 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, const auto params_charges = params.charges(); const auto params_ljs = params.ljs(); - this->cljs = QVector>(nats, std::make_tuple(0.0, 0.0, 0.0)); + this->cljs = QVector>(nats, boost::make_tuple(0.0, 0.0, 0.0)); auto cljs_data = cljs.data(); for (int i = 0; i < nats; ++i) @@ -513,11 +657,12 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, sig = 1e-9; } - cljs_data[i] = std::make_tuple(chg, sig, eps); + cljs_data[i] = boost::make_tuple(chg, sig, eps); } this->bond_params.clear(); this->constraints.clear(); + this->perturbable_constraints.clear(); // initialise all atoms as being unbonded this->unbonded_atoms.reserve(nats); @@ -540,6 +685,13 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, include_constrained_energies = map["include_constrained_energies"].value().asABoolean(); } + bool dynamic_constraints = true; + + if (map.specified("dynamic_constraints")) + { + dynamic_constraints = map["dynamic_constraints"].value().asABoolean(); + } + for (auto it = params.bonds().constBegin(); it != params.bonds().constEnd(); ++it) @@ -562,7 +714,7 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, this->unbonded_atoms.remove(atom1); } - const bool has_light_atom = (masses_data[atom0] < 2.5 or masses_data[atom1] < 2.5); + const bool has_light_atom = (light_atoms.contains(atom0) or light_atoms.contains(atom1)); const bool has_massless_atom = masses_data[atom0] < 0.5 or masses_data[atom1] < 0.5; auto this_constraint_type = constraint_type; @@ -576,26 +728,56 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, if ((not has_massless_atom) and ((this_constraint_type & CONSTRAIN_BONDS) or (has_light_atom and (this_constraint_type & CONSTRAIN_HBONDS)))) { - // add the constraint - this constrains the bond to whatever length it has now - const auto delta = coords[atom1] - coords[atom0]; - auto constraint_length = std::sqrt((delta[0] * delta[0]) + - (delta[1] * delta[1]) + - (delta[2] * delta[2])); + bool should_constrain_bond = true; - // use the r0 for the bond if this is close to the measured length and this - // is not a perturbable molecule - if (not is_perturbable and std::abs(constraint_length - r0) < 0.01) + double r0_1 = r0; + + if (is_perturbable) { - constraint_length = r0; + // we need to see if this bond is being perturbed + const auto bondparam1 = params1.bonds().value(it.key()).first; + + double k_1 = bondparam1.k() * bond_k_to_openmm; + r0_1 = bondparam1.r0() * bond_r0_to_openmm; + + if (std::abs(k_1 - k) > 1e-3 or std::abs(r0_1 - r0) > 1e-3) + { + // this is a perturbing bond + if (this_constraint_type & CONSTRAIN_NOT_PERTURBED) + { + // don't constrain a perturbing bond + should_constrain_bond = false; + } + else if ((this_constraint_type & CONSTRAIN_NOT_HEAVY_PERTURBED) and has_light_atom) + { + // constrain a perturbing bond involving hydrogen + should_constrain_bond = true; + } + } } - this->constraints.append(std::make_tuple(atom0, atom1, constraint_length)); - constrained_pairs.insert(to_pair(atom0, atom1)); - bond_is_not_constrained = false; + if (should_constrain_bond) + { + if (dynamic_constraints and (std::abs(r0 - r0_1) > 1e-4)) // match to somd1 + { + // this is a dynamic constraint that should change with lambda + this->perturbable_constraints.append(boost::make_tuple(atom0, atom1, r0, r0_1)); + } + else + { + // use the r0 for the bond + this->constraints.append(boost::make_tuple(atom0, atom1, r0)); + } + + constrained_pairs.insert(to_pair(atom0, atom1)); + bond_is_not_constrained = false; + } } if (include_constrained_energies or bond_is_not_constrained) - this->bond_params.append(std::make_tuple(atom0, atom1, r0, k)); + { + this->bond_params.append(boost::make_tuple(atom0, atom1, r0, k)); + } } // now the angles @@ -620,7 +802,8 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, const double k = angparam.k() * angle_k_to_openmm; const double theta0 = angparam.theta0(); // already in radians - const bool is_h_x_h = masses_data[atom0] < 2.5 and masses_data[atom2] < 2.5; + const bool is_h_x_h = light_atoms.contains(atom0) and light_atoms.contains(atom2); + const bool has_light_atom = is_h_x_h or light_atoms.contains(atom1); const auto key = to_pair(atom0, atom2); @@ -638,32 +821,73 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, // only include the angle X-y-Z if X-Z are not already constrained if ((this_constraint_type & CONSTRAIN_HANGLES) and is_h_x_h) { - const auto delta = coords[atom2] - coords[atom0]; - auto constraint_length = std::sqrt((delta[0] * delta[0]) + - (delta[1] * delta[1]) + - (delta[2] * delta[2])); - - // we can speed up OpenMM by making sure that constraints are - // equal if they operate on similar molecules (e.g. all water - // constraints are the same. We will check for this if this is - // a non-perturbable small molecule - if (mol.nAtoms() < 10 and not is_perturbable) + bool should_constrain_angle = true; + double theta0_1 = theta0; + + if (is_perturbable) { - constraint_length = getSharedConstraintLength(constraint_length); + // we need to see if this angle is being perturbed - if so, then don't constraint + auto angparam1 = params1.angles().value(it.key()); + + double k_1 = angparam.k() * angle_k_to_openmm; + theta0_1 = angparam.theta0(); + + if (std::abs(k_1 - k) > 1e-3 or std::abs(theta0_1 - theta0) > 1e-3) + { + // this angle perturbs + if (this_constraint_type & CONSTRAIN_NOT_PERTURBED) + { + // don't constrain a perturbing angle + should_constrain_angle = false; + } + else if ((this_constraint_type & CONSTRAIN_NOT_HEAVY_PERTURBED) and has_light_atom) + { + // constrain a perturbing angle involving hydrogen + should_constrain_angle = true; + } + } } - constraints.append(std::make_tuple(atom0, atom2, - constraint_length)); - constrained_pairs.insert(key); - angle_is_not_constrained = false; + if (should_constrain_angle) + { + if (dynamic_constraints and (theta0_1 != theta0)) + { + throw SireError::incomplete_code(QObject::tr( + "Dynamic constraints for angles are not yet implemented. " + "Either switch off dynamics constraints, or don't constrain " + "angles. If you want dynamic angle constraint support to " + "be added to sire then please raise a feature request issue " + "following the instructions at https://sire.openbiosim.org"), + CODELOC); + } + + const auto delta = coords[atom2] - coords[atom0]; + auto constraint_length = std::sqrt((delta[0] * delta[0]) + + (delta[1] * delta[1]) + + (delta[2] * delta[2])); + + // we can speed up OpenMM by making sure that constraints are + // equal if they operate on similar molecules (e.g. all water + // constraints are the same. We will check for this if this is + // a non-perturbable small molecule + if (mol.nAtoms() < 10 and not is_perturbable) + { + constraint_length = getSharedConstraintLength(constraint_length); + } + + constraints.append(boost::make_tuple(atom0, atom2, + constraint_length)); + constrained_pairs.insert(key); + angle_is_not_constrained = false; + } } } else angle_is_not_constrained = false; if (include_constrained_energies or angle_is_not_constrained) - ang_params.append(std::make_tuple(atom0, atom1, atom2, - theta0, k)); + ang_params.append(boost::make_tuple(atom0, atom1, atom2, + theta0, k)); } // now the dihedrals @@ -697,17 +921,17 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, if (periodicity > 0) { - dih_params.append(std::make_tuple(atom0, atom1, - atom2, atom3, - periodicity, phase, v)); + dih_params.append(boost::make_tuple(atom0, atom1, + atom2, atom3, + periodicity, phase, v)); } else if (periodicity == 0 and v == 0) { // this is a null dihedral, e.g. for perturbation. Make sure // that the periodicity is 1, else otherwise openmm will complain - dih_params.append(std::make_tuple(atom0, atom1, - atom2, atom3, - 1, phase, v)); + dih_params.append(boost::make_tuple(atom0, atom1, + atom2, atom3, + 1, phase, v)); } else { @@ -737,17 +961,17 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, if (periodicity > 0) { - dih_params.append(std::make_tuple(atom0, atom1, - atom2, atom3, - periodicity, phase, v)); + dih_params.append(boost::make_tuple(atom0, atom1, + atom2, atom3, + periodicity, phase, v)); } else if (periodicity == 0 and v == 0) { // this is a null dihedral, e.g. for perturbation. Make sure // that the periodicity is 1, else otherwise openmm will complain - dih_params.append(std::make_tuple(atom0, atom1, - atom2, atom3, - 1, phase, v)); + dih_params.append(boost::make_tuple(atom0, atom1, + atom2, atom3, + 1, phase, v)); } else { @@ -759,19 +983,19 @@ void OpenMMMolecule::constructFromAmber(const Molecule &mol, this->buildExceptions(mol, constrained_pairs, map); } -bool is_ghost(const std::tuple &clj) +bool is_ghost(const boost::tuple &clj) { - return std::get<0>(clj) == 0 and (std::get<1>(clj) == 0 or std::get<2>(clj) == 0); + return boost::get<0>(clj) == 0 and (boost::get<1>(clj) == 0 or boost::get<2>(clj) == 0); } -bool is_ghost_charge(const std::tuple &clj) +bool is_ghost_charge(const boost::tuple &clj) { - return std::get<0>(clj) == 0; + return boost::get<0>(clj) == 0; } -bool is_ghost_lj(const std::tuple &clj) +bool is_ghost_lj(const boost::tuple &clj) { - return std::get<1>(clj) == 0 or std::get<2>(clj) == 0; + return boost::get<1>(clj) == 0 or boost::get<2>(clj) == 0; } /** Go through all of the internals and compare them to the perturbed @@ -793,7 +1017,9 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) CODELOC); this->alphas = QVector(cljs.count(), 0.0); + this->kappas = QVector(cljs.count(), 0.0); this->perturbed->alphas = this->alphas; + this->perturbed->kappas = this->kappas; for (int i = 0; i < cljs.count(); ++i) { @@ -805,17 +1031,31 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) if (is_ghost(clj0)) { from_ghost_idxs.insert(i); + + // alpha is 1 for the reference state for ghost atoms + // (and will be 0 for the perturbed state) this->alphas[i] = 1.0; + + // kappa is 1 for both end states for ghost atoms + this->kappas[i] = 1.0; + this->perturbed->kappas[i] = 1.0; } else if (is_ghost(clj1)) { to_ghost_idxs.insert(i); + + // alpha is 1 for the perturbed state for ghost atoms + // (and will be 0 for the reference state) this->perturbed->alphas[i] = 1.0; + + // kappa is 1 for both end states for ghost atoms + this->kappas[i] = 1.0; + this->perturbed->kappas[i] = 1.0; } } } - QVector> bond_params_1; + QVector> bond_params_1; bond_params_1.reserve(bond_params.count()); QVector found_index_0(bond_params.count(), false); @@ -825,8 +1065,8 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) { const auto &bond0 = bond_params.at(i); - int atom0 = std::get<0>(bond0); - int atom1 = std::get<1>(bond0); + int atom0 = boost::get<0>(bond0); + int atom1 = boost::get<1>(bond0); bool found = false; @@ -836,7 +1076,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) { const auto &bond1 = perturbed->bond_params.at(j); - if (std::get<0>(bond1) == atom0 and std::get<1>(bond1) == atom1) + if (boost::get<0>(bond1) == atom0 and boost::get<1>(bond1) == atom1) { // we have found the matching bond! bond_params_1.append(bond1); @@ -852,7 +1092,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) { // add a null bond with the same r0, but null k found_index_0[i] = true; - bond_params_1.append(std::tuple(atom0, atom1, std::get<2>(bond0), 0.0)); + bond_params_1.append(boost::tuple(atom0, atom1, boost::get<2>(bond0), 0.0)); } } @@ -863,11 +1103,11 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) // need to add a bond missing in the reference state const auto &bond1 = perturbed->bond_params.at(j); - int atom0 = std::get<0>(bond1); - int atom1 = std::get<1>(bond1); + int atom0 = boost::get<0>(bond1); + int atom1 = boost::get<1>(bond1); // add a null bond with the same r0, but null k - bond_params.append(std::tuple(atom0, atom1, std::get<2>(bond1), 0.0)); + bond_params.append(boost::tuple(atom0, atom1, boost::get<2>(bond1), 0.0)); bond_params_1.append(bond1); found_index_1[j] = true; @@ -887,7 +1127,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) perturbed->bond_params = bond_params_1; - QVector> ang_params_1; + QVector> ang_params_1; ang_params_1.reserve(ang_params.count()); found_index_0 = QVector(ang_params.count(), false); @@ -897,9 +1137,9 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) { const auto &ang0 = ang_params.at(i); - int atom0 = std::get<0>(ang0); - int atom1 = std::get<1>(ang0); - int atom2 = std::get<2>(ang0); + int atom0 = boost::get<0>(ang0); + int atom1 = boost::get<1>(ang0); + int atom2 = boost::get<2>(ang0); bool found = false; @@ -909,7 +1149,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) { const auto &ang1 = perturbed->ang_params.at(j); - if (std::get<0>(ang1) == atom0 and std::get<1>(ang1) == atom1 and std::get<2>(ang1) == atom2) + if (boost::get<0>(ang1) == atom0 and boost::get<1>(ang1) == atom1 and boost::get<2>(ang1) == atom2) { // we have found the matching angle! ang_params_1.append(ang1); @@ -925,7 +1165,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) { // add a null angle with the same theta0, but null k found_index_0[i] = true; - ang_params_1.append(std::tuple(atom0, atom1, atom2, std::get<3>(ang0), 0.0)); + ang_params_1.append(boost::tuple(atom0, atom1, atom2, boost::get<3>(ang0), 0.0)); } } @@ -936,12 +1176,12 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) // need to add a bond missing in the reference state const auto &ang1 = perturbed->ang_params.at(j); - int atom0 = std::get<0>(ang1); - int atom1 = std::get<1>(ang1); - int atom2 = std::get<2>(ang1); + int atom0 = boost::get<0>(ang1); + int atom1 = boost::get<1>(ang1); + int atom2 = boost::get<2>(ang1); // add a null angle with the same theta0, but null k - ang_params.append(std::tuple(atom0, atom1, atom2, std::get<3>(ang1), 0.0)); + ang_params.append(boost::tuple(atom0, atom1, atom2, boost::get<3>(ang1), 0.0)); ang_params_1.append(ang1); found_index_1[j] = true; @@ -958,7 +1198,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) perturbed->ang_params = ang_params_1; - QVector> dih_params_1; + QVector> dih_params_1; dih_params_1.reserve(dih_params.count()); found_index_0 = QVector(dih_params.count(), false); @@ -968,10 +1208,10 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) { const auto &dih0 = dih_params.at(i); - int atom0 = std::get<0>(dih0); - int atom1 = std::get<1>(dih0); - int atom2 = std::get<2>(dih0); - int atom3 = std::get<3>(dih0); + int atom0 = boost::get<0>(dih0); + int atom1 = boost::get<1>(dih0); + int atom2 = boost::get<2>(dih0); + int atom3 = boost::get<3>(dih0); bool found = false; @@ -982,9 +1222,9 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) const auto &dih1 = perturbed->dih_params.at(j); // we need to match all of the atoms AND the periodicity - if (std::get<0>(dih1) == atom0 and std::get<1>(dih1) == atom1 and - std::get<2>(dih1) == atom2 and std::get<3>(dih1) == atom3 and - std::get<4>(dih0) == std::get<4>(dih1)) + if (boost::get<0>(dih1) == atom0 and boost::get<1>(dih1) == atom1 and + boost::get<2>(dih1) == atom2 and boost::get<3>(dih1) == atom3 and + boost::get<4>(dih0) == boost::get<4>(dih1)) { // we have found the matching torsion! dih_params_1.append(dih1); @@ -999,7 +1239,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) if (not found) { // add a null dihedral with the same periodicity and phase, but null k - dih_params_1.append(std::tuple(atom0, atom1, atom2, atom3, std::get<4>(dih0), std::get<5>(dih0), 0.0)); + dih_params_1.append(boost::tuple(atom0, atom1, atom2, atom3, boost::get<4>(dih0), boost::get<5>(dih0), 0.0)); found_index_0[i] = true; } } @@ -1011,13 +1251,13 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) // need to add a dihedral missing in the reference state const auto &dih1 = perturbed->dih_params.at(j); - int atom0 = std::get<0>(dih1); - int atom1 = std::get<1>(dih1); - int atom2 = std::get<2>(dih1); - int atom3 = std::get<3>(dih1); + int atom0 = boost::get<0>(dih1); + int atom1 = boost::get<1>(dih1); + int atom2 = boost::get<2>(dih1); + int atom3 = boost::get<3>(dih1); // add a null dihedral with the same periodicity and phase, but null k - dih_params.append(std::tuple(atom0, atom1, atom2, atom3, std::get<4>(dih1), std::get<5>(dih1), 0.0)); + dih_params.append(boost::tuple(atom0, atom1, atom2, atom3, boost::get<4>(dih1), boost::get<5>(dih1), 0.0)); dih_params_1.append(dih1); found_index_1[j] = true; } @@ -1035,7 +1275,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) // now align all of the exceptions - this should allow the bonding // to change during the perturbation - QVector> exception_params_1; + QVector> exception_params_1; exception_params_1.reserve(exception_params.count()); found_index_0 = QVector(exception_params.count(), false); @@ -1045,8 +1285,8 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) { const auto &ex0 = exception_params.at(i); - int atom0 = std::get<0>(ex0); - int atom1 = std::get<1>(ex0); + int atom0 = boost::get<0>(ex0); + int atom1 = boost::get<1>(ex0); bool found = false; @@ -1054,7 +1294,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) { const auto &ex1 = perturbed->exception_params.at(j); - if (std::get<0>(ex1) == atom0 and std::get<1>(ex1) == atom1) + if (boost::get<0>(ex1) == atom0 and boost::get<1>(ex1) == atom1) { // we have found the matching exception! exception_params_1.append(ex1); @@ -1068,7 +1308,7 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) if (not found) { // add a null exception with the same scale factors - exception_params_1.append(std::tuple(atom0, atom1, 1.0, 1.0)); + exception_params_1.append(boost::tuple(atom0, atom1, 1.0, 1.0)); found_index_0[i] = true; } } @@ -1080,11 +1320,11 @@ void OpenMMMolecule::alignInternals(const PropertyMap &map) // need to add an exception missing in the reference state const auto &ex1 = perturbed->exception_params.at(j); - int atom0 = std::get<0>(ex1); - int atom1 = std::get<1>(ex1); + int atom0 = boost::get<0>(ex1); + int atom1 = boost::get<1>(ex1); // add a null exception - exception_params.append(std::tuple(atom0, atom1, 1.0, 1.0)); + exception_params.append(boost::tuple(atom0, atom1, 1.0, 1.0)); exception_params_1.append(ex1); found_index_1[j] = true; } @@ -1141,7 +1381,7 @@ void OpenMMMolecule::buildExceptions(const Molecule &mol, const int i = atom0.value(); const int j = atom1.value(); - exception_params.append(std::make_tuple(i, j, cscl, ljscl)); + exception_params.append(boost::make_tuple(i, j, cscl, ljscl)); if (cscl == 0 and ljscl == 0) { @@ -1156,7 +1396,7 @@ void OpenMMMolecule::buildExceptions(const Molecule &mol, const auto length = std::sqrt((delta[0] * delta[0]) + (delta[1] * delta[1]) + (delta[2] * delta[2])); - constraints.append(std::make_tuple(i, j, getSharedConstraintLength(length))); + constraints.append(boost::make_tuple(i, j, getSharedConstraintLength(length))); constrained_pairs.insert(to_pair(i, j)); } } @@ -1174,7 +1414,7 @@ void OpenMMMolecule::buildExceptions(const Molecule &mol, else if (nats == 2) { // two atoms must be excluded - exception_params.append(std::make_tuple(0, 1, 0.0, 0.0)); + exception_params.append(boost::make_tuple(0, 1, 0.0, 0.0)); if (unbonded_atoms.contains(0) or unbonded_atoms.contains(1)) { @@ -1184,16 +1424,16 @@ void OpenMMMolecule::buildExceptions(const Molecule &mol, const auto length = std::sqrt((delta[0] * delta[0]) + (delta[1] * delta[1]) + (delta[2] * delta[2])); - constraints.append(std::make_tuple(0, 1, getSharedConstraintLength(length))); + constraints.append(boost::make_tuple(0, 1, getSharedConstraintLength(length))); constrained_pairs.insert(to_pair(0, 1)); } } else if (nats == 3) { // three atoms must be excluded - exception_params.append(std::make_tuple(0, 1, 0.0, 0.0)); - exception_params.append(std::make_tuple(1, 2, 0.0, 0.0)); - exception_params.append(std::make_tuple(0, 2, 0.0, 0.0)); + exception_params.append(boost::make_tuple(0, 1, 0.0, 0.0)); + exception_params.append(boost::make_tuple(1, 2, 0.0, 0.0)); + exception_params.append(boost::make_tuple(0, 2, 0.0, 0.0)); if (unbonded_atoms.contains(0) or unbonded_atoms.contains(1) or unbonded_atoms.contains(2)) { @@ -1203,21 +1443,21 @@ void OpenMMMolecule::buildExceptions(const Molecule &mol, auto length = std::sqrt((delta[0] * delta[0]) + (delta[1] * delta[1]) + (delta[2] * delta[2])); - constraints.append(std::make_tuple(0, 1, getSharedConstraintLength(length))); + constraints.append(boost::make_tuple(0, 1, getSharedConstraintLength(length))); constrained_pairs.insert(to_pair(0, 1)); delta = coords[2] - coords[0]; length = std::sqrt((delta[0] * delta[0]) + (delta[1] * delta[1]) + (delta[2] * delta[2])); - constraints.append(std::make_tuple(0, 2, getSharedConstraintLength(length))); + constraints.append(boost::make_tuple(0, 2, getSharedConstraintLength(length))); constrained_pairs.insert(to_pair(0, 2)); delta = coords[2] - coords[1]; length = std::sqrt((delta[0] * delta[0]) + (delta[1] * delta[1]) + (delta[2] * delta[2])); - constraints.append(std::make_tuple(1, 2, getSharedConstraintLength(length))); + constraints.append(boost::make_tuple(1, 2, getSharedConstraintLength(length))); constrained_pairs.insert(to_pair(1, 2)); } } @@ -1400,6 +1640,14 @@ QVector OpenMMMolecule::getAlphas() const return this->alphas; } +/** Return the kappa parameters for all atoms in atom order for + * this molecule + */ +QVector OpenMMMolecule::getKappas() const +{ + return this->kappas; +} + /** Return all of the atom charges in atom order for this molecule */ QVector OpenMMMolecule::getCharges() const { @@ -1412,7 +1660,7 @@ QVector OpenMMMolecule::getCharges() const for (int i = 0; i < natoms; ++i) { - charges_data[i] = std::get<0>(cljs_data[i]); + charges_data[i] = boost::get<0>(cljs_data[i]); } return charges; @@ -1430,7 +1678,7 @@ QVector OpenMMMolecule::getSigmas() const for (int i = 0; i < natoms; ++i) { - sigmas_data[i] = std::get<1>(cljs_data[i]); + sigmas_data[i] = boost::get<1>(cljs_data[i]); } return sigmas; @@ -1448,7 +1696,7 @@ QVector OpenMMMolecule::getEpsilons() const for (int i = 0; i < natoms; ++i) { - epsilons_data[i] = std::get<2>(cljs_data[i]); + epsilons_data[i] = boost::get<2>(cljs_data[i]); } return epsilons; @@ -1466,7 +1714,7 @@ QVector OpenMMMolecule::getBondKs() const for (int i = 0; i < nbonds; ++i) { - bond_ks_data[i] = std::get<3>(bond_params_data[i]); + bond_ks_data[i] = boost::get<3>(bond_params_data[i]); } return bond_ks; @@ -1484,7 +1732,7 @@ QVector OpenMMMolecule::getBondLengths() const for (int i = 0; i < nbonds; ++i) { - bond_lengths_data[i] = std::get<2>(bond_params_data[i]); + bond_lengths_data[i] = boost::get<2>(bond_params_data[i]); } return bond_lengths; @@ -1502,7 +1750,7 @@ QVector OpenMMMolecule::getAngleKs() const for (int i = 0; i < nangs; ++i) { - ang_ks_data[i] = std::get<4>(ang_params_data[i]); + ang_ks_data[i] = boost::get<4>(ang_params_data[i]); } return ang_ks; @@ -1520,7 +1768,7 @@ QVector OpenMMMolecule::getAngleSizes() const for (int i = 0; i < nangs; ++i) { - ang_sizes_data[i] = std::get<3>(ang_params_data[i]); + ang_sizes_data[i] = boost::get<3>(ang_params_data[i]); } return ang_sizes; @@ -1529,18 +1777,18 @@ QVector OpenMMMolecule::getAngleSizes() const /** Return all of the dihedral torsion periodicities in dihedral order * for this molecule */ -QVector OpenMMMolecule::getTorsionPeriodicities() const +QVector OpenMMMolecule::getTorsionPeriodicities() const { const int ndihs = this->dih_params.count(); - QVector dih_periodicities(ndihs); + QVector dih_periodicities(ndihs); auto dih_periodicities_data = dih_periodicities.data(); const auto dih_params_data = this->dih_params.constData(); for (int i = 0; i < ndihs; ++i) { - dih_periodicities_data[i] = std::get<4>(dih_params_data[i]); + dih_periodicities_data[i] = boost::get<4>(dih_params_data[i]); } return dih_periodicities; @@ -1560,7 +1808,7 @@ QVector OpenMMMolecule::getTorsionPhases() const for (int i = 0; i < ndihs; ++i) { - dih_phases_data[i] = std::get<5>(dih_params_data[i]); + dih_phases_data[i] = boost::get<5>(dih_params_data[i]); } return dih_phases; @@ -1580,7 +1828,7 @@ QVector OpenMMMolecule::getTorsionKs() const for (int i = 0; i < ndihs; ++i) { - dih_ks_data[i] = std::get<6>(dih_params_data[i]); + dih_ks_data[i] = boost::get<6>(dih_params_data[i]); } return dih_ks; @@ -1589,19 +1837,19 @@ QVector OpenMMMolecule::getTorsionKs() const /** Return the atom indexes of the atoms in the exceptions, in * exception order for this molecule */ -QVector> OpenMMMolecule::getExceptionAtoms() const +QVector> OpenMMMolecule::getExceptionAtoms() const { const int nexceptions = this->exception_params.count(); - QVector> exception_atoms(nexceptions); + QVector> exception_atoms(nexceptions); auto exception_atoms_data = exception_atoms.data(); const auto exception_params_data = this->exception_params.constData(); for (int i = 0; i < nexceptions; ++i) { - exception_atoms_data[i] = std::make_pair(std::get<0>(exception_params_data[i]), - std::get<1>(exception_params_data[i])); + exception_atoms_data[i] = std::make_pair(boost::get<0>(exception_params_data[i]), + boost::get<1>(exception_params_data[i])); } return exception_atoms; @@ -1621,7 +1869,7 @@ QVector OpenMMMolecule::getChargeScales() const for (int i = 0; i < nexceptions; ++i) { - charge_scales_data[i] = std::get<2>(exception_params_data[i]); + charge_scales_data[i] = boost::get<2>(exception_params_data[i]); } return charge_scales; @@ -1641,7 +1889,7 @@ QVector OpenMMMolecule::getLJScales() const for (int i = 0; i < nexceptions; ++i) { - lj_scales_data[i] = std::get<3>(exception_params_data[i]); + lj_scales_data[i] = boost::get<3>(exception_params_data[i]); } return lj_scales; @@ -1653,11 +1901,21 @@ QVector OpenMMMolecule::getLJScales() const /** Null constructor */ PerturbableOpenMMMolecule::PerturbableOpenMMMolecule() + : ConcreteProperty() { } +/** Construct from a passed molecule and map */ +PerturbableOpenMMMolecule::PerturbableOpenMMMolecule(const Molecule &mol, + const PropertyMap &map) + : ConcreteProperty() +{ + this->operator=(PerturbableOpenMMMolecule(OpenMMMolecule(mol, map))); +} + /** Construct from the passed OpenMMMolecule */ PerturbableOpenMMMolecule::PerturbableOpenMMMolecule(const OpenMMMolecule &mol) + : ConcreteProperty() { if (mol.perturbed.get() == 0) throw SireError::incompatible_error(QObject::tr( @@ -1665,9 +1923,45 @@ PerturbableOpenMMMolecule::PerturbableOpenMMMolecule(const OpenMMMolecule &mol) "OpenMMMolecule that has not been perturbed!"), CODELOC); + auto molecule = mol.atoms.molecule(); + + for (int i = 0; i < mol.atoms.count(); ++i) + { + perturbed_atoms.append(mol.atoms(i)); + } + + for (const auto &bond : mol.bond_params) + { + const auto &atom0 = boost::get<0>(bond); + const auto &atom1 = boost::get<1>(bond); + perturbed_bonds.append(SireMM::Bond(perturbed_atoms[atom0], perturbed_atoms[atom1])); + } + + for (const auto &ang : mol.ang_params) + { + const auto &atom0 = boost::get<0>(ang); + const auto &atom1 = boost::get<1>(ang); + const auto &atom2 = boost::get<2>(ang); + perturbed_angs.append(SireMM::Angle(perturbed_atoms[atom0], perturbed_atoms[atom1], + perturbed_atoms[atom2])); + } + + for (const auto &dih : mol.dih_params) + { + const auto &atom0 = boost::get<0>(dih); + const auto &atom1 = boost::get<1>(dih); + const auto &atom2 = boost::get<2>(dih); + const auto &atom3 = boost::get<3>(dih); + perturbed_dihs.append(SireMM::Dihedral(perturbed_atoms[atom0], perturbed_atoms[atom1], + perturbed_atoms[atom2], perturbed_atoms[atom3])); + } + alpha0 = mol.getAlphas(); alpha1 = mol.perturbed->getAlphas(); + kappa0 = mol.getKappas(); + kappa1 = mol.perturbed->getKappas(); + chg0 = mol.getCharges(); chg1 = mol.perturbed->getCharges(); @@ -1708,11 +2002,19 @@ PerturbableOpenMMMolecule::PerturbableOpenMMMolecule(const OpenMMMolecule &mol) to_ghost_idxs = mol.to_ghost_idxs; from_ghost_idxs = mol.from_ghost_idxs; + + perturbable_constraints = mol.perturbable_constraints; } /** Copy constructor */ PerturbableOpenMMMolecule::PerturbableOpenMMMolecule(const PerturbableOpenMMMolecule &other) - : alpha0(other.alpha0), alpha1(other.alpha1), + : ConcreteProperty(other), + perturbed_atoms(other.perturbed_atoms), + perturbed_bonds(other.perturbed_bonds), + perturbed_angs(other.perturbed_angs), + perturbed_dihs(other.perturbed_dihs), + alpha0(other.alpha0), alpha1(other.alpha1), + kappa0(other.kappa0), kappa1(other.kappa1), chg0(other.chg0), chg1(other.chg1), sig0(other.sig0), sig1(other.sig1), eps0(other.eps0), eps1(other.eps1), @@ -1727,7 +2029,9 @@ PerturbableOpenMMMolecule::PerturbableOpenMMMolecule(const PerturbableOpenMMMole charge_scl0(other.charge_scl0), charge_scl1(other.charge_scl1), lj_scl0(other.lj_scl0), lj_scl1(other.lj_scl1), to_ghost_idxs(other.to_ghost_idxs), from_ghost_idxs(other.from_ghost_idxs), - exception_atoms(other.exception_atoms), exception_idxs(other.exception_idxs) + exception_atoms(other.exception_atoms), exception_idxs(other.exception_idxs), + perturbable_constraints(other.perturbable_constraints), + constraint_idxs(other.constraint_idxs) { } @@ -1739,12 +2043,23 @@ PerturbableOpenMMMolecule::~PerturbableOpenMMMolecule() /** Comparison operator */ bool PerturbableOpenMMMolecule::operator==(const PerturbableOpenMMMolecule &other) const { - return alpha0 == alpha1 and chg0 == chg1 and sig0 == sig1 and eps0 == eps1 and - bond_k0 == bond_k1 and bond_r0 == bond_r1 and ang_k0 == ang_k1 and - ang_t0 == ang_t1 and tors_k0 == tors_k1 and tors_periodicity0 == tors_periodicity1 and - tors_phase0 == tors_phase1 and charge_scl0 == charge_scl1 and lj_scl0 == lj_scl1 and + return alpha0 == other.alpha0 and alpha1 == other.alpha1 and + kappa0 == other.kappa0 and kappa1 == other.kappa1 and + chg0 == other.chg0 and chg1 == other.chg1 and + sig0 == other.sig0 and sig1 == other.sig1 and + eps0 == other.eps0 and eps1 == other.eps1 and + bond_k0 == other.bond_k0 and bond_k1 == other.bond_k1 and + bond_r0 == other.bond_r0 and bond_r1 == other.bond_r1 and + ang_k0 == other.ang_k0 and ang_k1 == other.ang_k1 and + ang_t0 == other.ang_t0 and ang_t1 == other.ang_t1 and + tors_k0 == other.tors_k0 and tors_k1 == other.tors_k1 and + tors_periodicity0 == other.tors_periodicity0 and tors_periodicity1 == other.tors_periodicity1 and + tors_phase0 == other.tors_phase0 and tors_phase1 == other.tors_phase1 and + charge_scl0 == other.charge_scl0 and charge_scl1 == other.charge_scl1 and + lj_scl0 == other.lj_scl0 and lj_scl1 == other.lj_scl1 and to_ghost_idxs == other.to_ghost_idxs and from_ghost_idxs == other.from_ghost_idxs and - exception_atoms == other.exception_atoms and exception_idxs == other.exception_idxs; + exception_atoms == other.exception_atoms and exception_idxs == other.exception_idxs and + perturbable_constraints == other.perturbable_constraints and constraint_idxs == other.constraint_idxs; } /** Comparison operator */ @@ -1753,6 +2068,92 @@ bool PerturbableOpenMMMolecule::operator!=(const PerturbableOpenMMMolecule &othe return not this->operator==(other); } +PerturbableOpenMMMolecule &PerturbableOpenMMMolecule::operator=(const PerturbableOpenMMMolecule &other) +{ + if (this != &other) + { + perturbed_atoms = other.perturbed_atoms; + perturbed_bonds = other.perturbed_bonds; + perturbed_angs = other.perturbed_angs; + perturbed_dihs = other.perturbed_dihs; + + alpha0 = other.alpha0; + alpha1 = other.alpha1; + + kappa0 = other.kappa0; + kappa1 = other.kappa1; + + chg0 = other.chg0; + chg1 = other.chg1; + + sig0 = other.sig0; + sig1 = other.sig1; + + eps0 = other.eps0; + eps1 = other.eps1; + + bond_k0 = other.bond_k0; + bond_k1 = other.bond_k1; + + bond_r0 = other.bond_r0; + bond_r1 = other.bond_r1; + + ang_k0 = other.ang_k0; + ang_k1 = other.ang_k1; + + ang_t0 = other.ang_t0; + ang_t1 = other.ang_t1; + + tors_k0 = other.tors_k0; + tors_k1 = other.tors_k1; + + tors_periodicity0 = other.tors_periodicity0; + tors_periodicity1 = other.tors_periodicity1; + + tors_phase0 = other.tors_phase0; + tors_phase1 = other.tors_phase1; + + charge_scl0 = other.charge_scl0; + charge_scl1 = other.charge_scl1; + + lj_scl0 = other.lj_scl0; + lj_scl1 = other.lj_scl1; + + to_ghost_idxs = other.to_ghost_idxs; + from_ghost_idxs = other.from_ghost_idxs; + + exception_atoms = other.exception_atoms; + exception_idxs = other.exception_idxs; + + perturbable_constraints = other.perturbable_constraints; + constraint_idxs = other.constraint_idxs; + + Property::operator=(other); + } + + return *this; +} + +const char *PerturbableOpenMMMolecule::typeName() +{ + return "SireOpenMM::PerturbableOpenMMMolecule"; +} + +const char *PerturbableOpenMMMolecule::what() const +{ + return PerturbableOpenMMMolecule::typeName(); +} + +QString PerturbableOpenMMMolecule::toString() const +{ + return QString("PerturbableOpenMMMolecule()"); +} + +PerturbableOpenMMMolecule *PerturbableOpenMMMolecule::clone() const +{ + return new PerturbableOpenMMMolecule(*this); +} + /** Return the alpha parameters of the reference state */ QVector PerturbableOpenMMMolecule::getAlphas0() const { @@ -1765,6 +2166,18 @@ QVector PerturbableOpenMMMolecule::getAlphas1() const return this->alpha1; } +/** Return the kappa parameters of the reference state */ +QVector PerturbableOpenMMMolecule::getKappas0() const +{ + return this->kappa0; +} + +/** Return the kappa parameters of the perturbed state */ +QVector PerturbableOpenMMMolecule::getKappas1() const +{ + return this->kappa1; +} + /** Return the atom charges of the reference state */ QVector PerturbableOpenMMMolecule::getCharges0() const { @@ -1862,13 +2275,13 @@ QVector PerturbableOpenMMMolecule::getTorsionKs1() const } /** Return the torsion periodicity parameters of the reference state */ -QVector PerturbableOpenMMMolecule::getTorsionPeriodicities0() const +QVector PerturbableOpenMMMolecule::getTorsionPeriodicities0() const { return this->tors_periodicity0; } /** Return the torsion periodicity parameters of the perturbed state */ -QVector PerturbableOpenMMMolecule::getTorsionPeriodicities1() const +QVector PerturbableOpenMMMolecule::getTorsionPeriodicities1() const { return this->tors_periodicity1; } @@ -1912,7 +2325,7 @@ QVector PerturbableOpenMMMolecule::getLJScales1() const /** Return the indexes of the atoms that are to be ghosted in the * perturbed state */ -QSet PerturbableOpenMMMolecule::getToGhostIdxs() const +QSet PerturbableOpenMMMolecule::getToGhostIdxs() const { return this->to_ghost_idxs; } @@ -1920,7 +2333,7 @@ QSet PerturbableOpenMMMolecule::getToGhostIdxs() const /** Return the indexes of the atoms that were ghosts in the * reference state */ -QSet PerturbableOpenMMMolecule::getFromGhostIdxs() const +QSet PerturbableOpenMMMolecule::getFromGhostIdxs() const { return this->from_ghost_idxs; } @@ -1933,7 +2346,7 @@ bool PerturbableOpenMMMolecule::isGhostAtom(int atom) const } /** Return the indices of the atoms in the exceptions */ -QVector> PerturbableOpenMMMolecule::getExceptionAtoms() const +QVector> PerturbableOpenMMMolecule::getExceptionAtoms() const { return this->exception_atoms; } @@ -1941,7 +2354,7 @@ QVector> PerturbableOpenMMMolecule::getExceptionAtoms() cons /** Return the global indexes of the exceptions in the non-bonded and * ghost-14 forces */ -QVector> PerturbableOpenMMMolecule::getExceptionIndicies(const QString &name) const +QVector> PerturbableOpenMMMolecule::getExceptionIndicies(const QString &name) const { return this->exception_idxs.value(name); } @@ -1950,7 +2363,7 @@ QVector> PerturbableOpenMMMolecule::getExceptionIndicies(con * ghost-14 forces */ void PerturbableOpenMMMolecule::setExceptionIndicies(const QString &name, - const QVector> &exception_idxs) + const QVector> &exception_idxs) { if (exception_idxs.count() != this->exception_atoms.count()) throw SireError::incompatible_error(QObject::tr( @@ -1961,3 +2374,104 @@ void PerturbableOpenMMMolecule::setExceptionIndicies(const QString &name, this->exception_idxs.insert(name, exception_idxs); } + +/** Set the indexes of perturbable constraints in the System */ +void PerturbableOpenMMMolecule::setConstraintIndicies(const QVector &idxs) +{ + if (idxs.count() != perturbable_constraints.count()) + throw SireError::incompatible_error(QObject::tr( + "The number of constraint indicies (%1) does not match the number of constraints (%2)") + .arg(idxs.count()) + .arg(perturbable_constraints.count()), + CODELOC); + + this->constraint_idxs = idxs; +} + +/** Return the indicies of the perturbable constraints */ +QVector PerturbableOpenMMMolecule::getConstraintIndicies() const +{ + return this->constraint_idxs; +} + +/** Return the atom indexes of all of the constraints, with + * the constraint lengths at the two end states, in the order + * they appear in this molecule + */ +QVector> +PerturbableOpenMMMolecule::getPerturbableConstraintsWithAtoms() const +{ + return perturbable_constraints; +} + +/** Return three arrays containing the constraint indexes, and the + * reference and perturbed values of the constraint lengths + */ +boost::tuple, QVector, QVector> +PerturbableOpenMMMolecule::getPerturbableConstraints() const +{ + const int nconstraints = this->constraint_idxs.count(); + + if (nconstraints == 0) + { + return boost::make_tuple(QVector(), QVector(), QVector()); + } + + QVector r0, r1; + QVector idxs; + + r0.reserve(nconstraints); + r1.reserve(nconstraints); + idxs.reserve(nconstraints); + + for (int i = 0; i < nconstraints; ++i) + { + const auto &idx = this->constraint_idxs[i]; + + if (idx >= 0) + { + idxs.append(idx); + + const auto &constraint = this->perturbable_constraints[i]; + + r0.append(boost::get<2>(constraint)); + r1.append(boost::get<3>(constraint)); + } + } + + return boost::make_tuple(idxs, r0, r1); +} + +/** Return the atoms which are perturbed, in the order they are + * set in this perturbation + */ +QList PerturbableOpenMMMolecule::atoms() const +{ + return perturbed_atoms; +} + +/** Return the bonds which are perturbed, in the order they are + * set in this perturbation + */ +QList PerturbableOpenMMMolecule::bonds() const +{ + return perturbed_bonds; +} + +/** Return the angles which are perturbed, in the order they are + * set in this perturbation + */ +QList PerturbableOpenMMMolecule::angles() const +{ + return perturbed_angs; +} + +/** Return the torsions which are perturbed, in the order they are + * set in this perturbation. Note that this include both the + * normal dihedrals and the improper torsions (openmm internally + * treats them the same) + */ +QList PerturbableOpenMMMolecule::torsions() const +{ + return perturbed_dihs; +} diff --git a/wrapper/Convert/SireOpenMM/openmmmolecule.h b/wrapper/Convert/SireOpenMM/openmmmolecule.h index 5241dc8ff..8215ed7d9 100644 --- a/wrapper/Convert/SireOpenMM/openmmmolecule.h +++ b/wrapper/Convert/SireOpenMM/openmmmolecule.h @@ -11,6 +11,11 @@ #include "SireMM/mmdetail.h" #include "SireMM/excludedpairs.h" #include "SireMM/amberparams.h" +#include "SireMM/bond.h" +#include "SireMM/angle.h" +#include "SireMM/dihedral.h" + +#include SIRE_BEGIN_HEADER @@ -27,20 +32,18 @@ namespace SireOpenMM public: enum CONSTRAIN_TYPE { - CONSTRAIN_NONE = 0x0000, - CONSTRAIN_BONDS = 0x0001, - CONSTRAIN_HBONDS = 0x0010, - CONSTRAIN_HANGLES = 0x1000 + CONSTRAIN_NONE = 0x00000000, + CONSTRAIN_BONDS = 0x00000001, + CONSTRAIN_HBONDS = 0x00000010, + CONSTRAIN_HANGLES = 0x00001000, + CONSTRAIN_NOT_PERTURBED = 0x00010000, + CONSTRAIN_NOT_HEAVY_PERTURBED = 0x00100000, }; OpenMMMolecule(); OpenMMMolecule(const SireMol::Molecule &mol, const SireBase::PropertyMap &map); - OpenMMMolecule(const SireMol::Molecule &mol, - const SireBase::PropertyMap &map0, - const SireBase::PropertyMap &map1); - ~OpenMMMolecule(); bool operator==(const OpenMMMolecule &other) const; @@ -55,6 +58,7 @@ namespace SireOpenMM QVector getSigmas() const; QVector getEpsilons() const; QVector getAlphas() const; + QVector getKappas() const; QVector getBondKs() const; QVector getBondLengths() const; @@ -62,18 +66,18 @@ namespace SireOpenMM QVector getAngleKs() const; QVector getAngleSizes() const; - QVector getTorsionPeriodicities() const; + QVector getTorsionPeriodicities() const; QVector getTorsionPhases() const; QVector getTorsionKs() const; - QVector> getExceptionAtoms() const; + QVector> getExceptionAtoms() const; QVector getChargeScales() const; QVector getLJScales() const; bool isGhostAtom(int atom) const; - std::tuple + boost::tuple getException(int atom0, int atom1, int start_index, double coul_14_scl, @@ -108,36 +112,36 @@ namespace SireOpenMM QVector masses; /** Indexes of light atoms */ - QList light_atoms; - - /** Indexes of virtual sites */ - QList virtual_sites; + QSet light_atoms; /** Charge and LJ parameters (sigma / epsilon) */ - QVector> cljs; + QVector> cljs; /** Set of 1-4 or excluded pairs (with coulomb and LJ scaling factors) */ - QVector> exception_params; + QVector> exception_params; /** All the bond parameters */ - QVector> bond_params; + QVector> bond_params; /** All the angle parameters */ - QVector> ang_params; + QVector> ang_params; /** All the dihedral and improper parameters */ - QVector> dih_params; + QVector> dih_params; /** All the constraints */ - QVector> constraints; + QVector> constraints; + + /** All of the perturbable constraints - these include the r0 values */ + QVector> perturbable_constraints; /** The molecule perturbed molecule, if this is perturbable */ std::shared_ptr perturbed; /** The indicies of the added exceptions - only populated * if this is a peturbable molecule */ - QHash>> exception_idxs; + QHash>> exception_idxs; /** The property map used to get the perturbable properties - * this is only non-default if the molecule is perturbable @@ -152,15 +156,20 @@ namespace SireOpenMM */ QVector alphas; + /** Kappa values for all of the atoms. This is equal to zero for + * non-ghost atoms, and one for ghost atoms + */ + QVector kappas; + /** The indexes of atoms that become ghosts in the * perturbed state */ - QSet to_ghost_idxs; + QSet to_ghost_idxs; /** The indexes of atoms that are ghosts in the reference * state and are real in the perturbed state */ - QSet from_ghost_idxs; + QSet from_ghost_idxs; /** What type of constraint to use */ qint32 constraint_type; @@ -188,12 +197,16 @@ namespace SireOpenMM * in easy-to-access arrays, with guarantees that the arrays are * compatible and the data is aligned. */ - class PerturbableOpenMMMolecule + class PerturbableOpenMMMolecule : public SireBase::ConcreteProperty { public: PerturbableOpenMMMolecule(); PerturbableOpenMMMolecule(const OpenMMMolecule &mol); + + PerturbableOpenMMMolecule(const SireMol::Molecule &mol, + const SireBase::PropertyMap &map); + PerturbableOpenMMMolecule(const PerturbableOpenMMMolecule &other); ~PerturbableOpenMMMolecule(); @@ -201,9 +214,22 @@ namespace SireOpenMM bool operator==(const PerturbableOpenMMMolecule &other) const; bool operator!=(const PerturbableOpenMMMolecule &other) const; + PerturbableOpenMMMolecule &operator=(const PerturbableOpenMMMolecule &other); + + static const char *typeName(); + + const char *what() const; + + QString toString() const; + + PerturbableOpenMMMolecule *clone() const; + QVector getAlphas0() const; QVector getAlphas1() const; + QVector getKappas0() const; + QVector getKappas1() const; + QVector getCharges0() const; QVector getCharges1() const; @@ -224,8 +250,8 @@ namespace SireOpenMM QVector getTorsionKs0() const; QVector getTorsionKs1() const; - QVector getTorsionPeriodicities0() const; - QVector getTorsionPeriodicities1() const; + QVector getTorsionPeriodicities0() const; + QVector getTorsionPeriodicities1() const; QVector getTorsionPhases0() const; QVector getTorsionPhases1() const; @@ -234,23 +260,57 @@ namespace SireOpenMM QVector getLJScales0() const; QVector getLJScales1() const; - QSet getToGhostIdxs() const; - QSet getFromGhostIdxs() const; + QSet getToGhostIdxs() const; + QSet getFromGhostIdxs() const; bool isGhostAtom(int atom) const; - QVector> getExceptionAtoms() const; + QVector> getExceptionAtoms() const; - QVector> getExceptionIndicies(const QString &name) const; + QVector> getExceptionIndicies(const QString &name) const; void setExceptionIndicies(const QString &name, - const QVector> &exception_idxs); + const QVector> &exception_idxs); + + void setConstraintIndicies(const QVector &constraint_idxs); + + QVector getConstraintIndicies() const; + + boost::tuple, QVector, QVector> getPerturbableConstraints() const; + + QVector> getPerturbableConstraintsWithAtoms() const; + + QList atoms() const; + QList bonds() const; + QList angles() const; + QList torsions() const; private: + /** The atoms that are perturbed, in the order they appear + * in the arrays below + */ + QList perturbed_atoms; + + /** The bonds that are perturbed, in the order they appear + * in the arrays below + */ + QList perturbed_bonds; + + /** The angles that are perturbed, in the order they appear + * in the arrays below + */ + QList perturbed_angs; + + /** The torsions that are perturbed, in the order they appear + * in the arrays below + */ + QList perturbed_dihs; + /** The array of parameters for the two end states, aligned * so that they can be morphed via the LambdaLever */ QVector alpha0, alpha1; + QVector kappa0, kappa1; QVector chg0, chg1; QVector sig0, sig1; QVector eps0, eps1; @@ -259,7 +319,7 @@ namespace SireOpenMM QVector ang_k0, ang_k1; QVector ang_t0, ang_t1; QVector tors_k0, tors_k1; - QVector tors_periodicity0, tors_periodicity1; + QVector tors_periodicity0, tors_periodicity1; QVector tors_phase0, tors_phase1; QVector charge_scl0, charge_scl1; QVector lj_scl0, lj_scl1; @@ -267,23 +327,37 @@ namespace SireOpenMM /** The indexes of atoms that become ghosts in the * perturbed state */ - QSet to_ghost_idxs; + QSet to_ghost_idxs; /** The indexes of atoms that are ghosts in the reference * state and are real in the perturbed state */ - QSet from_ghost_idxs; + QSet from_ghost_idxs; /** The indicies of the atoms in the exceptions, in exception order */ - QVector> exception_atoms; + QVector> exception_atoms; /** The indicies of the added exceptions - only populated * if this is a peturbable molecule */ - QHash>> exception_idxs; + QHash>> exception_idxs; + + /** All of the perturbable constraints - these include the r0 values + * for both end states + */ + QVector> perturbable_constraints; + + /** The indicies of the added constraints - this should be equal + * to the number of perturbable constraints in the molecule + */ + QVector constraint_idxs; }; } +Q_DECLARE_METATYPE(SireOpenMM::PerturbableOpenMMMolecule) + +SIRE_EXPOSE_CLASS(SireOpenMM::PerturbableOpenMMMolecule) + SIRE_END_HEADER #endif diff --git a/wrapper/Convert/SireOpenMM/register_extras.cpp b/wrapper/Convert/SireOpenMM/register_extras.cpp new file mode 100644 index 000000000..88a50155d --- /dev/null +++ b/wrapper/Convert/SireOpenMM/register_extras.cpp @@ -0,0 +1,48 @@ + +#include "register_extras.h" + +#include "boost/python.hpp" + +#include + +namespace bp = boost::python; + +using namespace SireOpenMM; + +/** Thanks to this page for instructions on how to convert from SWIG to Boost + * https://wiki.python.org/moin/boost.python/HowTo + */ +struct PySwigObject +{ + PyObject_HEAD void *ptr; + const char *desc; +}; + +void *extract_swig_wrapped_pointer(PyObject *obj) +{ + char thisStr[] = "this"; + + // first we need to get the this attribute from the Python Object + if (!PyObject_HasAttrString(obj, thisStr)) + return NULL; + + PyObject *thisAttr = PyObject_GetAttrString(obj, thisStr); + if (thisAttr == NULL) + return NULL; + + // This Python Object is a SWIG Wrapper and contains our pointer + void *pointer = ((PySwigObject *)thisAttr)->ptr; + Py_DECREF(thisAttr); + return pointer; +} + +namespace SireOpenMM +{ + void register_extras() + { + bp::converter::registry::insert(&extract_swig_wrapped_pointer, bp::type_id()); + bp::converter::registry::insert(&extract_swig_wrapped_pointer, bp::type_id()); + bp::converter::registry::insert(&extract_swig_wrapped_pointer, bp::type_id()); + bp::converter::registry::insert(&extract_swig_wrapped_pointer, bp::type_id()); + } +} diff --git a/wrapper/Convert/SireOpenMM/register_extras.h b/wrapper/Convert/SireOpenMM/register_extras.h new file mode 100644 index 000000000..1ab1897f0 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/register_extras.h @@ -0,0 +1,9 @@ +#ifndef SIREOPENMM_REGISTER_EXTRAS_H +#define SIREOPENMM_REGISTER_EXTRAS_H + +namespace SireOpenMM +{ + void register_extras(); +} + +#endif diff --git a/wrapper/Convert/SireOpenMM/scanheaders.py b/wrapper/Convert/SireOpenMM/scanheaders.py new file mode 100644 index 000000000..6dbb008b2 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/scanheaders.py @@ -0,0 +1,527 @@ +########################################## +# +# This script scans all of the header +# files in a specified directory to +# get the list of classes that should +# be exposed in Python +# + +import sys +import os +import re +import pickle +import string + +from glob import glob + + +def getFiles(dir, pattern): + files = glob("%s/%s" % (dir, pattern)) + + trimmed_files = [] + + for file in files: + trimmed_files.append(file[len(dir) + 1 :]) + + return trimmed_files + + +def getIncludes(file, lines): + includes = {'"%s"' % file: 1} + + for line in lines: + if line.find("CONDITIONAL_INCLUDE") == -1: + m = re.search(r"#include\s+([\"|<].*[\"|>])", line) + + if m: + includes[m.groups()[0]] = 1 + + ret = list(includes.keys()) + ret.sort() + return ret + + +def getDependencies(dir, file): + """Return the list of header files included by the .cpp or .c file + that corresponds to 'file'""" + + try: + # is there a corresponding .cpp file? + file_cpp = re.sub(r"h(p*)$", "cpp", file) + + lines = open("%s/%s" % (dir, file_cpp), "r").readlines() + return getIncludes(file, lines) + + except: + pass + + try: + # is there a corresponding .c file? + file_c = re.sub(r"h(p*)$", "c", file) + + lines = open("%s/%s" % (dir, file_c), "r").readlines() + return getIncludes(file, lines) + + except: + pass + + return ['"%s"' % file] + + +class Properties: + def __init__(self): + self._dependencies = {} + self._properties = [] + + def addProperty(self, property, alias): + self._properties.append((property, alias)) + + def properties(self): + return self._properties + + def addDependency(self, headerfile, dir, module_dir): + deps = getDependencies(dir, headerfile) + getDependencies( + module_dir, headerfile + ) + + for dep in deps: + self._dependencies[dep] = 1 + + def dependencies(self): + return list(self._dependencies.keys()) + + +skip_metatypes = [ + "QVariant", + "SireCAS::ExpressionBase", + "SireMaths::Rational", + "SireCluster::Node", + "SireCluster::Nodes", + "SireBase::PropertyPtr", +] + + +class HeaderInfo: + def __init__(self, filename, dir, module_dir): + self._filename = filename + self._dependencies = getDependencies(dir, filename) + getDependencies( + module_dir, filename + ) + self._classes = [] + self._functions = [] + self._aliases = {} + self._properties = [] + self._metatypes = [] + + def addClass(self, classname): + self._classes.append(classname) + + def addFunction(self, func): + self._functions.append(func) + + def addMetaType(self, classname): + # don't register some types + if classname in skip_metatypes: + return + + self._metatypes.append(classname) + + def addAlias(self, classname, alias): + self._aliases[classname] = alias + + def addProperty(self, prop, propbase): + self._properties.append((prop, propbase)) + + def dependencies(self): + return self._dependencies + + def classes(self): + return self._classes + + def functions(self): + return self._functions + + def metaTypes(self): + return self._metatypes + + def aliases(self): + return self._aliases + + def properties(self): + return self._properties + + def hasProperties(self): + return len(self._properties) > 0 + + +match_class = r"SIREN*_EXPOSE_CLASS\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_alias = r"SIREN*_EXPOSE_ALIAS\(\s*\n*\s*\(?([<>,\-\s\w\d:]+)\)?\s*\n*,\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_function = r"SIREN*_EXPOSE_FUNCTION\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_property = r"SIREN*_EXPOSE_PROPERTY\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*,\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_atom_property = r"SIRE_EXPOSE_ATOM_PROPERTY\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*,\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_res_property = r"SIRE_EXPOSE_RESIDUE_PROPERTY\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*,\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_cg_property = r"SIRE_EXPOSE_CUTGROUP_PROPERTY\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*,\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_chain_property = r"SIRE_EXPOSE_CHAIN_PROPERTY\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*,\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_seg_property = r"SIRE_EXPOSE_SEGMENT_PROPERTY\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*,\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_bead_property = r"SIRE_EXPOSE_BEAD_PROPERTY\(\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*,\s*\n*\s*\(?([<>,\s\w\d:]+)\)?\s*\n*\s*\)" +match_metatype = r"Q_DECLARE_METATYPE\(\s*\n*\s*\(?([<>.\s\w\d:]+)\)?\s*\n*\s*\)" + +db = {} + + +def add_doc(function, docs, db): + # now try to extract the function signature + # - first, remove any derived bases from constructor lines + function = function.split(" : ")[0] + + match = re.search(r"([\d_\w]+)::([\d_\w]+)\((.*)\)", function) + + if match: + cls = match.groups()[0].lstrip().rstrip() + nam = match.groups()[1].lstrip().rstrip() + targs = match.groups()[2].split(",") + + args = [] + + i = 0 + while i < len(targs): + arg = targs[i].lstrip().rstrip() + + if arg.find("<") != -1: + nopen = arg.count("<") - arg.count(">") + + # template - see if there are multiple template arguments + while nopen > 0 and (i + 1) < len(targs): + next_arg = targs[i + 1].lstrip().rstrip() + arg += ", " + next_arg + i += 1 + nopen += next_arg.count("<") - next_arg.count(">") + + if len(arg) > 0: + args.append(arg) + + i += 1 + + if not cls in db: + db[cls] = {} + + if not nam in db[cls]: + db[cls][nam] = {} + + nargs = len(args) + + if not nargs in db[cls][nam]: + db[cls][nam][nargs] = [] + + db[cls][nam][nargs].append((args, docs)) + + +def extract_docs(filename, db): + read_from = open(filename, "r") + + # concat to one line + file_str = "" + for line in read_from.readlines(): + file_str += line + + # break off all code + file_str = re.sub("{", "\n{", file_str) + + # remove '//' comments + file_str = re.sub("//.*", "", file_str) + + # split on '\n' + file_as_list = file_str.splitlines(True) + + # strip everything + for index in range(len(file_as_list)): + file_as_list[index] = file_as_list[index].strip() + + doc = "" + function = "" + mode = 0 # 0 is nothing, 1 is comment, 2 is function + + # add in newlines where appropriate + for line in file_as_list: + line = line.lstrip().rstrip() + + added = False + + if line.startswith("/*"): + mode = 1 + doc = line + added = True + + elif line.startswith("{"): + # print("code... (%s)" % mode) + if mode == 2: + # hopefully we have seen a function + add_doc(function, doc, db) + doc = "" + function = "" + + mode = 0 + + if line.endswith("*/"): + # end of comment - look for a function + mode = 2 + if not added: + doc += "\n" + line + added = True + # print("completed comment\n%s" % doc) + + elif line.endswith(";") or line.endswith("}"): + # print("line... (%s)" % mode) + mode = 0 + + else: + if not added: + if mode == 1: + doc += "\n" + line + elif mode == 2: + if len(function) == 0: + function = line + else: + function += " " + line + + # print("Function | %s" % function) + + +def scanFiles( + dir, + module_dir, + atom_properties, + cg_properties, + res_properties, + chain_properties, + seg_properties, + bead_properties, +): + """Scan the header files in the passed directory to get information + about all of the exposed classes, returning a list of all of + the classes that are being exposed, and placing meta information + into the directory 'module_dir'""" + + h_files = getFiles(dir, "*.h") + getFiles(module_dir, "*.h") + hpp_files = getFiles(dir, "*.hpp") + getFiles(module_dir, "*.hpp") + cpp_files = getFiles(dir, "*.cpp") + + # dictionary mapping files to exposed classes + active_files = {} + + # the list of exposed classes + exposed_classes = [] + + # the list of classes that have been registered with QMetaType + meta_classes = [] + + # database of all documentation + doc_db = {} + + # read through each .cpp file, looking for documentation + for file in cpp_files: + try: + extract_docs("%s/%s" % (dir, file), doc_db) + except Exception as e: + print("Problem parsing %s | %s" % (file, e)) + pass + + # read each file, looking for SIRE_EXPOSE_FUNCTION or SIRE_EXPOSE_CLASS + for file in h_files + hpp_files: + if file.find("sirenglobal.h") != -1: + continue + + try: + lines = open("%s/%s" % (dir, file), "r").readlines() + except: + lines = open("%s/%s" % (module_dir, file), "r").readlines() + + text = " ".join(lines) + + for m in re.finditer(match_class, text): + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + active_files[file].addClass(m.groups()[0].strip()) + exposed_classes.append(m.groups()[0].strip()) + + for m in re.finditer(match_alias, text): + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + active_files[file].addClass(m.groups()[0].strip()) + active_files[file].addAlias(m.groups()[0].strip(), m.groups()[1].strip()) + exposed_classes.append(m.groups()[0].strip()) + + for m in re.finditer(match_function, text): + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + active_files[file].addFunction(m.groups()[0].strip()) + + for m in re.finditer(match_metatype, text): + # don't match the 'errors.h' files, as these are wrapped separately + if file == "errors.h": + continue + + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + active_files[file].addMetaType(m.groups()[0].strip()) + + for m in re.finditer(match_property, text): + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + active_files[file].addProperty(m.groups()[0].strip(), m.groups()[1].strip()) + + for m in re.finditer(match_atom_property, text): + atom_properties.addDependency(file, dir, module_dir) + atom_properties.addProperty(m.groups()[0].strip(), m.groups()[1].strip()) + + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + classname = m.groups()[1].split("::")[-1].strip() + + active_files[file].addClass(classname) + active_files[file].addAlias(classname, classname) + exposed_classes.append(classname) + + for m in re.finditer(match_cg_property, text): + cg_properties.addDependency(file, dir, module_dir) + cg_properties.addProperty(m.groups()[0].strip(), m.groups()[1].strip()) + + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + classname = m.groups()[1].split("::")[-1].strip() + + active_files[file].addClass(classname) + active_files[file].addAlias(classname, classname) + exposed_classes.append(classname) + + for m in re.finditer(match_res_property, text): + res_properties.addDependency(file, dir, module_dir) + res_properties.addProperty(m.groups()[0].strip(), m.groups()[1].strip()) + + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + classname = m.groups()[1].split("::")[-1].strip() + + active_files[file].addClass(classname) + active_files[file].addAlias(classname, classname) + exposed_classes.append(classname) + + for m in re.finditer(match_chain_property, text): + chain_properties.addDependency(file, dir, module_dir) + chain_properties.addProperty(m.groups()[0].strip(), m.groups()[1].strip()) + + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + classname = m.groups()[1].split("::")[-1].strip() + + active_files[file].addClass(classname) + active_files[file].addAlias(classname, classname) + exposed_classes.append(classname) + + for m in re.finditer(match_seg_property, text): + seg_properties.addDependency(file, dir, module_dir) + seg_properties.addProperty(m.groups()[0].strip(), m.groups()[1].strip()) + + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + classname = m.groups()[1].split("::")[-1].strip() + + active_files[file].addClass(classname) + active_files[file].addAlias(classname, classname) + exposed_classes.append(classname) + + for m in re.finditer(match_bead_property, text): + bead_properties.addDependency(file, dir, module_dir) + bead_properties.addProperty(m.groups()[0].strip(), m.groups()[1].strip()) + + if file not in active_files: + active_files[file] = HeaderInfo(file, dir, module_dir) + + classname = m.groups()[1].split("::")[-1].strip() + + active_files[file].addClass(classname) + active_files[file].addAlias(classname, classname) + exposed_classes.append(classname) + + # now add each active file to a single header file that can be parsed by Py++ + FILE = open("%s/active_headers.h" % module_dir, "w") + + print( + "#ifndef ACTIVE_HEADERS_H\n" + + "#define ACTIVE_HEADERS_H\n\n" + + "#ifdef GCCXML_PARSE\n", + file=FILE, + ) + + files = list(active_files.keys()) + files.sort() + + for file in files: + print('#include "%s"' % file, file=FILE) + + print("\n#endif\n\n#endif", file=FILE) + + FILE.close() + + # now write out the active_files data structure so it can be + # used by other scripts + FILE = open("%s/active_headers.data" % module_dir, "wb") + pickle.dump(active_files, FILE) + FILE.close() + + # now write out the documentation data so it can be used by other scripts + FILE = open("%s/docs.data" % module_dir, "wb") + pickle.dump(doc_db, FILE) + FILE.close() + + return exposed_classes + + +if __name__ == "__main__": + siredir = "." + outdir = "." + + exposed_classes = {} + + atom_properties = Properties() + cg_properties = Properties() + res_properties = Properties() + chain_properties = Properties() + seg_properties = Properties() + bead_properties = Properties() + + FILE = open("module_info", "w") + + print("Module SireOpenMM", file=FILE) + print("Source SireOpenMM", file=FILE) + print("Root ../../../corelib/src/libs", file=FILE) + + FILE.close() + + module_classes = scanFiles( + ".", + ".", + atom_properties, + cg_properties, + res_properties, + chain_properties, + seg_properties, + bead_properties, + ) + + for clas in module_classes: + exposed_classes[clas] = 1 + + # write the set of exposed classes to a data file to be used + # by other scripts + pickle.dump(exposed_classes, open("classdb.data", "wb")) diff --git a/wrapper/Convert/SireOpenMM/sire_openmm.cpp b/wrapper/Convert/SireOpenMM/sire_openmm.cpp index f2f58de39..29d374efc 100644 --- a/wrapper/Convert/SireOpenMM/sire_openmm.cpp +++ b/wrapper/Convert/SireOpenMM/sire_openmm.cpp @@ -74,6 +74,21 @@ namespace SireOpenMM { } + const char *OpenMMMetaData::typeName() + { + return "SireOpenMM::OpenMMMetaData"; + } + + const char *OpenMMMetaData::what() const + { + return OpenMMMetaData::typeName(); + } + + QString OpenMMMetaData::toString() const + { + return QString("OpenMMMetaData()"); + } + SireMol::SelectorM OpenMMMetaData::index() const { return atom_index; @@ -508,4 +523,9 @@ namespace SireOpenMM return SelectorMol(ret); } + SireUnits::Dimension::MolarEnergy get_potential_energy(OpenMM::Context &context) + { + return context.getState(OpenMM::State::Energy).getPotentialEnergy() * SireUnits::kJ_per_mol; + } + } // end of namespace SireOpenMM diff --git a/wrapper/Convert/SireOpenMM/sire_openmm.h b/wrapper/Convert/SireOpenMM/sire_openmm.h index 9e9dbd760..b083a2c20 100644 --- a/wrapper/Convert/SireOpenMM/sire_openmm.h +++ b/wrapper/Convert/SireOpenMM/sire_openmm.h @@ -48,6 +48,11 @@ namespace SireOpenMM const LambdaLever &lever); ~OpenMMMetaData(); + static const char *typeName(); + const char *what() const; + + QString toString() const; + bool hasCoordinates() const; bool hasVelocities() const; bool hasBoxVectors() const; @@ -69,7 +74,7 @@ namespace SireOpenMM LambdaLever lambda_lever; }; - SireMol::SelectorMol openmm_system_to_sire(const OpenMM::System &mols, + SireMol::SelectorMol openmm_system_to_sire(const OpenMM::System &system, const SireBase::PropertyMap &map); OpenMMMetaData sire_to_openmm_system(OpenMM::System &system, @@ -98,4 +103,15 @@ namespace SireOpenMM const QString &value); } +SIRE_EXPOSE_CLASS(SireOpenMM::OpenMMMetaData) + +SIRE_EXPOSE_FUNCTION(SireOpenMM::openmm_system_to_sire) +SIRE_EXPOSE_FUNCTION(SireOpenMM::sire_to_openmm_system) +SIRE_EXPOSE_FUNCTION(SireOpenMM::set_openmm_coordinates_and_velocities) +SIRE_EXPOSE_FUNCTION(SireOpenMM::get_potential_energy) +SIRE_EXPOSE_FUNCTION(SireOpenMM::extract_coordinates) +SIRE_EXPOSE_FUNCTION(SireOpenMM::extract_coordinates_and_velocities) +SIRE_EXPOSE_FUNCTION(SireOpenMM::extract_space) +SIRE_EXPOSE_FUNCTION(SireOpenMM::set_context_platform_property) + #endif diff --git a/wrapper/Convert/SireOpenMM/sire_to_openmm_system.cpp b/wrapper/Convert/SireOpenMM/sire_to_openmm_system.cpp index 92348aed1..8c399b5e7 100644 --- a/wrapper/Convert/SireOpenMM/sire_to_openmm_system.cpp +++ b/wrapper/Convert/SireOpenMM/sire_to_openmm_system.cpp @@ -33,6 +33,7 @@ #include "SireCAS/lambdaschedule.h" #include "SireMaths/vector.h" +#include "SireMaths/maths.h" #include "SireBase/parallel.h" #include "SireBase/propertylist.h" @@ -789,6 +790,7 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, if (any_perturbable) { lambda_lever.addLever("alpha"); + lambda_lever.addLever("kappa"); // somd uses a default shift_delta of 2.0 A SireUnits::Dimension::Length shift_delta = 2.0 * SireUnits::angstrom; @@ -806,6 +808,20 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, if (shift_delta.value() < 0) shift_delta = 0.0 * SireUnits::angstrom; + // somd uses a default shift_coulomb of 1 A + double shift_coulomb = SireMaths::pow_2((1 * SireUnits::angstrom).to(SireUnits::nanometer)); + + if (map.specified("shift_coulomb")) + { + const auto &value = map["shift_coulomb"].value(); + + if (value.isA()) + shift_coulomb = SireMaths::pow_2(value.asA().value().to(SireUnits::nanometer)); + else + shift_coulomb = SireMaths::pow_2( + value.asA().toUnit().to(SireUnits::nanometer)); + } + // use a Taylor LJ power of 1 int taylor_power = 1; @@ -875,10 +891,11 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, { nb14_expression = QString( "coul_nrg+lj_nrg;" - "coul_nrg=138.9354558466661*q*(((%1)/sqrt(alpha+r^2))-(1.0/r));" - "lj_nrg=four_epsilon*((sig6^2)-sig6);" - "sig6=(sigma^6)/(%2*sigma^6 + r^6);") + "coul_nrg=138.9354558466661*q*(((%1)/sqrt((%2*alpha)+r^2))-(kappa/r));" + "lj_nrg=four_epsilon*sig6*(sig6-1);" + "sig6=(sigma^6)/(%3*sigma^6 + r^6);") .arg(coulomb_power_expression("alpha", coulomb_power)) + .arg(shift_coulomb) .arg(taylor_power_expression("alpha", taylor_power)) .toStdString(); } @@ -886,11 +903,12 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, { nb14_expression = QString( "coul_nrg+lj_nrg;" - "coul_nrg=138.9354558466661*q*(((%1)/sqrt(alpha+r^2))-(1.0/r));" - "lj_nrg=four_epsilon*((sig6^2)-sig6);" + "coul_nrg=138.9354558466661*q*(((%1)/sqrt((%2*alpha)+r^2))-(kappa/r));" + "lj_nrg=four_epsilon*sig6*(sig6-1);" "sig6=(sigma^6)/(((sigma*delta) + r^2)^3);" - "delta=%2*alpha;") + "delta=%3*alpha;") .arg(coulomb_power_expression("alpha", coulomb_power)) + .arg(shift_coulomb) .arg(shift_delta.to(SireUnits::nanometer)) .toStdString(); } @@ -901,6 +919,7 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, ghost_14ff->addPerBondParameter("sigma"); ghost_14ff->addPerBondParameter("four_epsilon"); ghost_14ff->addPerBondParameter("alpha"); + ghost_14ff->addPerBondParameter("kappa"); // short-range intramolecular term that should not use // periodic boundaries or cutoffs @@ -916,7 +935,9 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, // V_{LJ}(r) = 4 epsilon [ (sigma^12 / (alpha^m sigma^6 + r^6)^2) - // (sigma^6 / (alpha^m sigma^6 + r^6) ) ] // - // V_{coul}(r) = (1-alpha)^n q_i q_j / 4 pi eps_0 (alpha+r^2)^(1/2) + // V_{coul}(r) = (1-alpha)^n q_i q_j / 4 pi eps_0 (delta+r^2)^(1/2) + // + // delta = shift_coulomb^2 * alpha // // Note that we supply half_sigma and two_sqrt_epsilon to save some // cycles @@ -928,12 +949,14 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, // kJ mol-1 given the units of charge (|e|) and distance (nm) // clj_expression = QString("coul_nrg+lj_nrg;" - "coul_nrg=138.9354558466661*q1*q2*(((%1)/sqrt(max_alpha+r^2))-(1.0/r));" - "lj_nrg=two_sqrt_epsilon1*two_sqrt_epsilon2*((sig6^2)-sig6);" - "sig6=(sigma^6)/(%2*sigma^6 + r^6);" + "coul_nrg=138.9354558466661*q1*q2*(((%1)/sqrt((%2*max_alpha)+r^2))-(max_kappa/r));" + "lj_nrg=two_sqrt_epsilon1*two_sqrt_epsilon2*sig6*(sig6-1);" + "sig6=(sigma^6)/(%3*sigma^6 + r^6);" + "max_kappa=max(kappa1, kappa2);" "max_alpha=max(alpha1, alpha2);" "sigma=half_sigma1+half_sigma2;") .arg(coulomb_power_expression("max_alpha", coulomb_power)) + .arg(shift_coulomb) .arg(taylor_power_expression("max_alpha", taylor_power)) .toStdString(); } @@ -948,7 +971,9 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, // // delta = shift_delta * alpha // - // V_{coul}(r) = (1-alpha)^n q_i q_j / 4 pi eps_0 (alpha+r^2)^(1/2) + // V_{coul}(r) = (1-alpha)^n q_i q_j / 4 pi eps_0 (delta+r^2)^(1/2) + // + // delta = shift_coulomb^2 * alpha // // Note that we pre-calculate delta as a forcefield parameter, // and also supply half_sigma and two_sqrt_epsilon to save some @@ -961,13 +986,15 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, // kJ mol-1 given the units of charge (|e|) and distance (nm) // clj_expression = QString("coul_nrg+lj_nrg;" - "coul_nrg=138.9354558466661*q1*q2*(((%1)/sqrt(max_alpha+r^2))-(1.0/r));" - "lj_nrg=two_sqrt_epsilon1*two_sqrt_epsilon2*((sig6^2)-sig6);" + "coul_nrg=138.9354558466661*q1*q2*(((%1)/sqrt((%2*max_alpha)+r^2))-(max_kappa/r));" + "lj_nrg=two_sqrt_epsilon1*two_sqrt_epsilon2*sig6*(sig6-1);" "sig6=(sigma^6)/(((sigma*delta) + r^2)^3);" - "delta=%2*max_alpha;" + "delta=%3*max_alpha;" + "max_kappa=max(kappa1, kappa2);" "max_alpha=max(alpha1, alpha2);" "sigma=half_sigma1+half_sigma2;") .arg(coulomb_power_expression("max_alpha", coulomb_power)) + .arg(shift_coulomb) .arg(shift_delta.to(SireUnits::nanometer)) .toStdString(); } @@ -979,11 +1006,13 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, ghost_ghostff->addPerParticleParameter("half_sigma"); ghost_ghostff->addPerParticleParameter("two_sqrt_epsilon"); ghost_ghostff->addPerParticleParameter("alpha"); + ghost_ghostff->addPerParticleParameter("kappa"); ghost_nonghostff->addPerParticleParameter("q"); ghost_nonghostff->addPerParticleParameter("half_sigma"); ghost_nonghostff->addPerParticleParameter("two_sqrt_epsilon"); ghost_nonghostff->addPerParticleParameter("alpha"); + ghost_nonghostff->addPerParticleParameter("kappa"); // this will be slow if switched on, as it needs recalculating // for every change in parameters @@ -1046,7 +1075,7 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, // just a holder for all of the custom parameters for the // ghost forces (prevents us having to continually re-allocate it) - std::vector custom_params = {0.0, 0.0, 0.0, 0.0}; + std::vector custom_params = {0.0, 0.0, 0.0, 0.0, 0.0}; // the sets of particle indexes for the ghost atoms and non-ghost atoms std::set ghost_atoms; @@ -1126,6 +1155,7 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, auto masses_data = mol.masses.constData(); auto cljs_data = mol.cljs.constData(); auto alphas_data = mol.alphas.constData(); + auto kappas_data = mol.kappas.constData(); if (any_perturbable and mol.isPerturbable()) { @@ -1155,13 +1185,15 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, const auto &clj = cljs_data[j]; // reduced_q - custom_params[0] = std::get<0>(clj); + custom_params[0] = boost::get<0>(clj); // half_sigma - custom_params[1] = 0.5 * std::get<1>(clj); + custom_params[1] = 0.5 * boost::get<1>(clj); // two_sqrt_epsilon - custom_params[2] = 2.0 * std::sqrt(std::get<2>(clj)); + custom_params[2] = 2.0 * std::sqrt(boost::get<2>(clj)); // alpha custom_params[3] = alphas_data[j]; + // kappa + custom_params[4] = kappas_data[j]; // Add the particle to the ghost and nonghost forcefields ghost_ghostff->addParticle(custom_params); @@ -1179,14 +1211,14 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, // calculated using the ghost forcefields // (the ghost forcefields include a coulomb term // that subtracts from whatever was calculated here) - cljff->addParticle(std::get<0>(clj), 0.0, 0.0); + cljff->addParticle(boost::get<0>(clj), 0.0, 0.0); } else { // this isn't a ghost atom. Record this fact and // just add it to the standard cljff as normal - cljff->addParticle(std::get<0>(clj), std::get<1>(clj), - std::get<2>(clj)); + cljff->addParticle(boost::get<0>(clj), boost::get<1>(clj), + boost::get<2>(clj)); non_ghost_atoms.insert(atom_index); } } @@ -1215,20 +1247,22 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, const auto &clj = cljs_data[j]; // Add the particle to the standard CLJ forcefield - cljff->addParticle(std::get<0>(clj), std::get<1>(clj), std::get<2>(clj)); + cljff->addParticle(boost::get<0>(clj), boost::get<1>(clj), boost::get<2>(clj)); // We need to add this molecule to the ghost and ghost // forcefields if there are any perturbable molecules if (any_perturbable) { // reduced charge - custom_params[0] = std::get<0>(clj); + custom_params[0] = boost::get<0>(clj); // half_sigma - custom_params[1] = 0.5 * std::get<1>(clj); + custom_params[1] = 0.5 * boost::get<1>(clj); // two_sqrt_epsilon - custom_params[2] = 2.0 * std::sqrt(std::get<2>(clj)); + custom_params[2] = 2.0 * std::sqrt(boost::get<2>(clj)); // alpha - is zero for non-ghost atoms custom_params[3] = 0.0; + // kappa - is 0 for non-ghost atoms + custom_params[4] = 0.0; ghost_ghostff->addParticle(custom_params); ghost_nonghostff->addParticle(custom_params); non_ghost_atoms.insert(atom_index); @@ -1239,34 +1273,34 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, // now add all of the bond parameters for (const auto &bond : mol.bond_params) { - bondff->addBond(std::get<0>(bond) + start_index, - std::get<1>(bond) + start_index, - std::get<2>(bond), std::get<3>(bond)); + bondff->addBond(boost::get<0>(bond) + start_index, + boost::get<1>(bond) + start_index, + boost::get<2>(bond), boost::get<3>(bond)); } // now add all of the angles for (const auto &ang : mol.ang_params) { - angff->addAngle(std::get<0>(ang) + start_index, - std::get<1>(ang) + start_index, - std::get<2>(ang) + start_index, - std::get<3>(ang), std::get<4>(ang)); + angff->addAngle(boost::get<0>(ang) + start_index, + boost::get<1>(ang) + start_index, + boost::get<2>(ang) + start_index, + boost::get<3>(ang), boost::get<4>(ang)); } // now add all of the dihedrals and impropers for (const auto &dih : mol.dih_params) { - dihff->addTorsion(std::get<0>(dih) + start_index, - std::get<1>(dih) + start_index, - std::get<2>(dih) + start_index, - std::get<3>(dih) + start_index, - std::get<4>(dih), std::get<5>(dih), std::get<6>(dih)); + dihff->addTorsion(boost::get<0>(dih) + start_index, + boost::get<1>(dih) + start_index, + boost::get<2>(dih) + start_index, + boost::get<3>(dih) + start_index, + boost::get<4>(dih), boost::get<5>(dih), boost::get<6>(dih)); } for (const auto &constraint : mol.constraints) { - const auto atom0 = std::get<0>(constraint); - const auto atom1 = std::get<1>(constraint); + const auto atom0 = boost::get<0>(constraint); + const auto atom1 = boost::get<1>(constraint); const auto mass0 = system.getParticleMass(atom0 + start_index); const auto mass1 = system.getParticleMass(atom1 + start_index); @@ -1276,7 +1310,7 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, system.addConstraint(atom0 + start_index, atom1 + start_index, - std::get<2>(constraint)); + boost::get<2>(constraint)); } // else we will need to think about how to constrain bonds // involving fixed atoms. Could we fix the other atom too? @@ -1314,7 +1348,7 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, /// for the molecules to the OpenMM forces /// - /// Stage 6 - Set up the exceptions + /// Stage 6 - Set up the exceptions and perturbable constraints /// /// We now have to add all of the exceptions to the non-bonded /// forces (including the ghost forces). Exceptions are overrides @@ -1323,29 +1357,55 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, /// of the entire function, as it involves lots of atom-atom /// pair loops and can create a large exception list /// + /// We will also add all of the perturbable constraints here + /// for (int i = 0; i < nmols; ++i) { int start_index = start_indexes[i]; const auto &mol = openmm_mols_data[i]; - QVector> exception_idxs; + QVector> exception_idxs; + QVector constraint_idxs; const bool is_perturbable = any_perturbable and mol.isPerturbable(); if (is_perturbable) { - exception_idxs = QVector>(mol.exception_params.count(), - std::make_pair(-1, -1)); + exception_idxs = QVector>(mol.exception_params.count(), + boost::make_tuple(-1, -1)); + constraint_idxs = QVector(mol.perturbable_constraints.count(), -1); + + // do all of the perturbable constraints + for (int j = 0; j < mol.perturbable_constraints.count(); ++j) + { + const auto &constraint = mol.perturbable_constraints[j]; + + const auto atom0 = boost::get<0>(constraint); + const auto atom1 = boost::get<1>(constraint); + + const auto mass0 = system.getParticleMass(atom0 + start_index); + const auto mass1 = system.getParticleMass(atom1 + start_index); + + if (mass0 != 0 and mass1 != 0) + { + // add the constraint using the reference state length + auto idx = system.addConstraint(atom0 + start_index, + atom1 + start_index, + boost::get<2>(constraint)); + + constraint_idxs[j] = idx; + } + } } for (int j = 0; j < mol.exception_params.count(); ++j) { const auto ¶m = mol.exception_params[j]; - const auto atom0 = std::get<0>(param); - const auto atom1 = std::get<1>(param); - const auto coul_14_scale = std::get<2>(param); - const auto lj_14_scale = std::get<3>(param); + const auto atom0 = boost::get<0>(param); + const auto atom1 = boost::get<1>(param); + const auto coul_14_scale = boost::get<2>(param); + const auto lj_14_scale = boost::get<3>(param); auto p = mol.getException(atom0, atom1, start_index, @@ -1366,8 +1426,8 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, // elsewhere - note that we need to use 1e-9 to // make sure that OpenMM doesn't eagerly remove // this, and cause "changed excluded atoms" warnings - idx = cljff->addException(std::get<0>(p), std::get<1>(p), - std::get<2>(p), 1e-9, + idx = cljff->addException(boost::get<0>(p), boost::get<1>(p), + boost::get<2>(p), 1e-9, 1e-9, true); if (coul_14_scl != 0 or lj_14_scl != 0) @@ -1376,10 +1436,10 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, // to the ghost-14 forcefield if (ghost_14ff != 0) { - // parameters are q, sigma, four_epsilon and alpha + // parameters are q, sigma, four_epsilon, alpha(0) and kappa(1) std::vector params14 = - {std::get<2>(p), std::get<3>(p), - 4.0 * std::get<4>(p), 0.0}; + {boost::get<2>(p), boost::get<3>(p), + 4.0 * boost::get<4>(p), 0.0, 1.0}; if (params14[0] == 0) // cannot use zero params in case they are @@ -1389,36 +1449,36 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, if (params14[1] == 0) params14[1] = 1e-9; - nbidx = ghost_14ff->addBond(std::get<0>(p), - std::get<1>(p), + nbidx = ghost_14ff->addBond(boost::get<0>(p), + boost::get<1>(p), params14); } } } else { - idx = cljff->addException(std::get<0>(p), std::get<1>(p), - std::get<2>(p), std::get<3>(p), - std::get<4>(p), true); + idx = cljff->addException(boost::get<0>(p), boost::get<1>(p), + boost::get<2>(p), boost::get<3>(p), + boost::get<4>(p), true); } // these are the indexes of the exception in the // non-bonded forcefields and also the ghost-14 forcefield - exception_idxs[j] = std::make_pair(idx, nbidx); + exception_idxs[j] = boost::make_tuple(idx, nbidx); } else { - cljff->addException(std::get<0>(p), std::get<1>(p), - std::get<2>(p), std::get<3>(p), - std::get<4>(p), true); + cljff->addException(boost::get<0>(p), boost::get<1>(p), + boost::get<2>(p), boost::get<3>(p), + boost::get<4>(p), true); } // we need to make sure that the list of exclusions in // the NonbondedForce match those in the CustomNonbondedForces if (ghost_ghostff != 0) { - ghost_ghostff->addExclusion(std::get<0>(p), std::get<1>(p)); - ghost_nonghostff->addExclusion(std::get<0>(p), std::get<1>(p)); + ghost_ghostff->addExclusion(boost::get<0>(p), boost::get<1>(p)); + ghost_nonghostff->addExclusion(boost::get<0>(p), boost::get<1>(p)); } } @@ -1427,6 +1487,9 @@ OpenMMMetaData SireOpenMM::sire_to_openmm_system(OpenMM::System &system, auto pert_idx = idx_to_pert_idx.value(i, openmm_mols.count() + 1); lambda_lever.setExceptionIndicies(pert_idx, "clj", exception_idxs); + + lambda_lever.setConstraintIndicies(pert_idx, + constraint_idxs); } } diff --git a/wrapper/Convert/SireOpenMM/special_code.py b/wrapper/Convert/SireOpenMM/special_code.py new file mode 100644 index 000000000..675b0bb11 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/special_code.py @@ -0,0 +1,20 @@ +############################################### +# +# This file contains special code to help +# with the wrapping of SireOpenMM classes +# +# + + +def fixMB(mb): + mb.add_declaration_code('#include "./register_extras.h"') + mb.add_registration_code("SireOpenMM::register_extras();") + + +def fix_OpenMM_include(c): + c.add_declaration_code('#include "OpenMM.h"') + + +special_code = { + "std::vector": fix_OpenMM_include, +} diff --git a/wrapper/Convert/SireOpenMM/vector_less__OpenMM_scope_Vec3__greater_.pypp.cpp b/wrapper/Convert/SireOpenMM/vector_less__OpenMM_scope_Vec3__greater_.pypp.cpp new file mode 100644 index 000000000..9a1052cec --- /dev/null +++ b/wrapper/Convert/SireOpenMM/vector_less__OpenMM_scope_Vec3__greater_.pypp.cpp @@ -0,0 +1,22 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#include "boost/python.hpp" +#include "boost/python/suite/indexing/vector_indexing_suite.hpp" +#include "vector_less__OpenMM_scope_Vec3__greater_.pypp.hpp" + +#include + +namespace bp = boost::python; + +void register_vector_less__OpenMM_scope_Vec3__greater__class(){ + + { //::std::vector< OpenMM::Vec3 > + typedef bp::class_< std::vector< OpenMM::Vec3 > > vector_less__OpenMM_scope_Vec3__greater__exposer_t; + vector_less__OpenMM_scope_Vec3__greater__exposer_t vector_less__OpenMM_scope_Vec3__greater__exposer = vector_less__OpenMM_scope_Vec3__greater__exposer_t( "vector_less__OpenMM_scope_Vec3__greater_", "" ); + bp::scope vector_less__OpenMM_scope_Vec3__greater__scope( vector_less__OpenMM_scope_Vec3__greater__exposer ); + vector_less__OpenMM_scope_Vec3__greater__exposer.def( bp::vector_indexing_suite< ::std::vector< OpenMM::Vec3 > >() ); + } + +} diff --git a/wrapper/Convert/SireOpenMM/vector_less__OpenMM_scope_Vec3__greater_.pypp.hpp b/wrapper/Convert/SireOpenMM/vector_less__OpenMM_scope_Vec3__greater_.pypp.hpp new file mode 100644 index 000000000..9e2086251 --- /dev/null +++ b/wrapper/Convert/SireOpenMM/vector_less__OpenMM_scope_Vec3__greater_.pypp.hpp @@ -0,0 +1,10 @@ +// This file has been generated by Py++. + +// (C) Christopher Woods, GPL >= 3 License + +#ifndef vector_less__OpenMM_scope_Vec3__greater__hpp__pyplusplus_wrapper +#define vector_less__OpenMM_scope_Vec3__greater__hpp__pyplusplus_wrapper + +void register_vector_less__OpenMM_scope_Vec3__greater__class(); + +#endif//vector_less__OpenMM_scope_Vec3__greater__hpp__pyplusplus_wrapper diff --git a/wrapper/Convert/__init__.py b/wrapper/Convert/__init__.py index 2385c329b..20980bffc 100644 --- a/wrapper/Convert/__init__.py +++ b/wrapper/Convert/__init__.py @@ -14,6 +14,10 @@ "sire_to_gemmi", "gemmi_to_sire", "supported_formats", + "LambdaLever", + "PerturbableOpenMMMolecule", + "OpenMMMetaData", + "SOMMContext", ] try: @@ -68,14 +72,39 @@ def smarts_to_rdkit(*args, **kwargs): try: + from ._SireOpenMM import sire_to_openmm_system as _sire_to_openmm_system + from ._SireOpenMM import openmm_system_to_sire as _openmm_system_to_sire + from ._SireOpenMM import extract_coordinates as _openmm_extract_coordinates from ._SireOpenMM import ( - _sire_to_openmm_system, - _openmm_system_to_sire, - _openmm_extract_coordinates, - _openmm_extract_coordinates_and_velocities, - _openmm_extract_space, - _minimise_openmm_context, + extract_coordinates_and_velocities as _openmm_extract_coordinates_and_velocities, ) + from ._SireOpenMM import extract_space as _openmm_extract_space + from ._SireOpenMM import minimise_openmm_context as _minimise_openmm_context + + from ._sommcontext import SOMMContext + from ._perturbablemol import ( + _changed_atoms, + _changed_bonds, + _changed_angles, + _changed_torsions, + _changed_exceptions, + _changed_constraints, + ) + + from ._SireOpenMM import LambdaLever, PerturbableOpenMMMolecule, OpenMMMetaData + + from ..._pythonize import _pythonize + + _pythonize( + [LambdaLever, PerturbableOpenMMMolecule, OpenMMMetaData], delete_old=True + ) + + PerturbableOpenMMMolecule.changed_atoms = _changed_atoms + PerturbableOpenMMMolecule.changed_bonds = _changed_bonds + PerturbableOpenMMMolecule.changed_angles = _changed_angles + PerturbableOpenMMMolecule.changed_torsions = _changed_torsions + PerturbableOpenMMMolecule.changed_exceptions = _changed_exceptions + PerturbableOpenMMMolecule.changed_constraints = _changed_constraints _has_openmm = True @@ -250,11 +279,11 @@ def sire_to_openmm(mols, map): # choose the constraint based on the timestep if timestep_in_fs > 4: # need constraint on everything - constraint = "bonds" + constraint = "bonds-not-heavy-perturbed" elif timestep_in_fs > 1: # need it just on H bonds and angles - constraint = "h-bonds" + constraint = "h-bonds-not-heavy-perturbed" else: # can get away with no constraints @@ -270,8 +299,8 @@ def sire_to_openmm(mols, map): ) if constraint == "auto": - # we don't apply the constraint to perturbable molecules - constraint = "none" + # only apply the constraint to non-perturbed hydrogens + constraint = "h-bonds-not-heavy-perturbed" map.set("perturbable_constraint", constraint) @@ -294,8 +323,16 @@ def sire_to_openmm(mols, map): "on a system with a non-periodic space." ) + barostat_freq = 25 + + if map.specified("barostat_frequency"): + barostat_freq = map["barostat_frequency"].value().as_integer() + pressure = ensemble.pressure().to(atm) * openmm.unit.atmosphere - system.addForce(openmm.MonteCarloBarostat(pressure, temperature)) + + barostat = openmm.MonteCarloBarostat(pressure, temperature, barostat_freq) + + system.addForce(barostat) platform = None @@ -461,6 +498,22 @@ def _no_openmm(): _has_openmm = False + class LambdaLever: + def __init__(self, *args, **kwargs): + _no_openmm() + + class PerturbableOpenMMMolecule: + def __init__(self, *args, **kwargs): + _no_openmm() + + class OpenMMMetaData: + def __init__(self, *args, **kwargs): + _no_openmm() + + class SOMMContext: + def __init__(self, *args, **kwargs): + _no_openmm() + def sire_to_openmm(*args, **kwargs): _no_openmm() diff --git a/wrapper/MM/SireMM_containers.cpp b/wrapper/MM/SireMM_containers.cpp index 0dc789227..052ea36dc 100644 --- a/wrapper/MM/SireMM_containers.cpp +++ b/wrapper/MM/SireMM_containers.cpp @@ -43,6 +43,13 @@ #include "SireMol/angleid.h" #include "SireMol/dihedralid.h" +#include "SireMol/core.h" + +#include "SireMM/bond.h" +#include "SireMM/angle.h" +#include "SireMM/dihedral.h" +#include "SireMM/improper.h" + #include "SireMM/ljparameter.h" #include "SireMM/twoatomfunctions.h" #include "SireMM/threeatomfunctions.h" @@ -61,43 +68,48 @@ using boost::python::register_tuple; void register_SireMM_containers() { - register_PackedArray>(); + register_PackedArray>(); + + register_tuple>(); - register_tuple>(); + register_list>>(); - register_list>>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); - register_list>(); + register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); - register_list>(); + register_list>(); - register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); - register_dict>(); - register_dict>(); - register_dict>(); - register_dict>(); - register_dict>(); + register_dict>(); + register_dict>(); + register_dict>(); + register_dict>(); + register_dict>(); - register_dict>(); - register_dict>(); - register_dict>(); - register_dict>(); - register_dict>(); + register_dict>(); + register_dict>(); + register_dict>(); + register_dict>(); + register_dict>(); - register_dict>(); - register_dict>(); - register_dict>(); + register_dict>(); + register_dict>(); + register_dict>(); } diff --git a/wrapper/Mol/MoleculeView.pypp.cpp b/wrapper/Mol/MoleculeView.pypp.cpp index c4f7778fc..5dc1e56d6 100644 --- a/wrapper/Mol/MoleculeView.pypp.cpp +++ b/wrapper/Mol/MoleculeView.pypp.cpp @@ -666,14 +666,14 @@ void register_MoleculeView_class(){ } { //::SireMol::MoleculeView::extract - typedef ::SireMol::Molecule ( ::SireMol::MoleculeView::*extract_function_type)( ) const; + typedef ::SireMol::Molecule ( ::SireMol::MoleculeView::*extract_function_type)( bool ) const; extract_function_type extract_function_value( &::SireMol::MoleculeView::extract ); MoleculeView_exposer.def( "extract" , extract_function_value - , bp::release_gil_policy() - , "Extract a copy of this view which contains only the currently\nselected atoms. This allows the used to pull out parts of a larger molecule,\ne.g. if they want to have only selected residues in a protein and do not\nwant to have to store or manipulate the larger protein molecule" ); + , ( bp::arg("to_same_molecule")=(bool)(false) ) + , "" ); } { //::SireMol::MoleculeView::getLink diff --git a/wrapper/Mol/SireMol_containers.cpp b/wrapper/Mol/SireMol_containers.cpp index f6ac584ef..07634129f 100644 --- a/wrapper/Mol/SireMol_containers.cpp +++ b/wrapper/Mol/SireMol_containers.cpp @@ -85,119 +85,121 @@ using boost::python::register_tuple; void register_SireMol_containers() { - register_viewsofmol_list(); + register_viewsofmol_list(); - register_list>(); - register_list>>(); - register_list>(); + register_list>(); + register_list>>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); + register_list>(); - register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>>(); - register_list>>(); + register_list>>(); + register_list>>(); - register_list>>(); + register_list>>(); - register_list>>(); + register_list>>(); - register_list>(); - register_list>>(); + register_list>(); + register_list>>(); - register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>>(); + register_list>>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_tuple>(); - register_tuple>(); - register_tuple>(); + register_list>(); - register_tuple>(); - register_tuple>(); + register_tuple>(); + register_tuple>(); + register_tuple>(); - register_tuple>(); + register_tuple>(); + register_tuple>(); - register_tuple>(); + register_tuple>(); - register_tuple>(); + register_tuple>(); - register_tuple>(); + register_tuple>(); - register_tuple>(); - register_tuple>(); - register_tuple, SireBase::PropertyMap>>(); - register_tuple, SireBase::PropertyMap>>(); + register_tuple>(); - register_PackedArray>(); - register_PackedArray>(); + register_tuple>(); + register_tuple>(); + register_tuple, SireBase::PropertyMap>>(); + register_tuple, SireBase::PropertyMap>>(); - register_dict>(); + register_PackedArray>(); + register_PackedArray>(); - register_dict>(); - register_dict>(); + register_dict>(); - register_dict>>(); + register_dict>(); + register_dict>(); - register_dict>>(); - register_dict>>(); - register_dict>>(); - register_dict>>(); - register_dict>>(); - register_dict>(); - register_dict>(); - register_dict>(); + register_dict>>(); - register_set>(); - register_set>(); + register_dict>>(); + register_dict>>(); + register_dict>>(); + register_dict>>(); + register_dict>>(); + register_dict>(); + register_dict>(); + register_dict>(); - register_set>(); - register_set>(); + register_set>(); + register_set>(); + + register_set>(); + register_set>(); } diff --git a/wrapper/Qt/sireqt_containers.cpp b/wrapper/Qt/sireqt_containers.cpp index 0e7e4051b..8e7c5c4d2 100644 --- a/wrapper/Qt/sireqt_containers.cpp +++ b/wrapper/Qt/sireqt_containers.cpp @@ -50,58 +50,60 @@ using boost::python::register_tuple; void register_SireQt_containers() { - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); - register_list>>(); - register_list>>>(); - register_list>>>>(); + register_list>(); + register_list>(); - register_list>(); - register_list>>(); + register_list>>(); + register_list>>>(); + register_list>>>>(); - register_list>(); - register_list>(); - register_list(); + register_list>(); + register_list>>(); - register_list>(); - register_list>(); + register_list>(); + register_list>(); + register_list(); - register_list>(); + register_list>(); + register_list>(); - register_tuple>(); - register_tuple>(); + register_list>(); - register_dict>>(); + register_tuple>(); + register_tuple>(); -#if QT_VERSION >= QT_VERSION_CHECK(4, 2, 0) - register_set>(); + register_tuple>(); + register_tuple>(); + register_tuple, QVector, QVector>>(); - register_dict>(); + register_list>>(); + register_list>>(); - register_dict>(); + register_dict>>(); -#else - register_set, QString>(); + register_set>(); - register_dict, QString, QVariant>(); + register_dict>(); - register_dict, QString, QString>(); + register_dict>(); -#endif + register_set>(); }