45#include "rnemd/RNEMD.hpp" 
   63#include "brains/Thermo.hpp" 
   64#include "io/Globals.hpp" 
   65#include "math/ConvexHull.hpp" 
   72#include "rnemd/RNEMDParameters.hpp" 
   73#include "types/FixedChargeAdapter.hpp" 
   74#include "types/FluctuatingChargeAdapter.hpp" 
   75#include "utils/Accumulator.hpp" 
   76#include "utils/AccumulatorView.hpp" 
   77#include "utils/Constants.hpp" 
   79using namespace OpenMD::Utils;
 
   81namespace OpenMD::RNEMD {
 
   84      info_(info), commonA_(info_), commonB_(info_), evaluator_(info_),
 
   85      seleMan_(info_), evaluatorA_(info_), evaluatorB_(info_), seleManA_(info_),
 
   86      seleManB_(info_), outputEvaluator_(info_), outputSeleMan_(info_) {
 
   91    Globals* simParams           = info->getSimParams();
 
   92    RNEMDParameters* rnemdParams = simParams->getRNEMDParameters();
 
   94    usePeriodicBoundaryConditions_ =
 
   95        simParams->getUsePeriodicBoundaryConditions();
 
   97    doRNEMD_ = rnemdParams->getUseRNEMD();
 
   98    if (!doRNEMD_) 
return;
 
  101    std::map<std::string, RNEMDFluxType> stringToFluxType;
 
  103    stringToFluxType[
"KE"]          = rnemdKE;
 
  104    stringToFluxType[
"Px"]          = rnemdPx;
 
  105    stringToFluxType[
"Py"]          = rnemdPy;
 
  106    stringToFluxType[
"Pz"]          = rnemdPz;
 
  107    stringToFluxType[
"Pvector"]     = rnemdPvector;
 
  108    stringToFluxType[
"Lx"]          = rnemdLx;
 
  109    stringToFluxType[
"Ly"]          = rnemdLy;
 
  110    stringToFluxType[
"Lz"]          = rnemdLz;
 
  111    stringToFluxType[
"Lvector"]     = rnemdLvector;
 
  112    stringToFluxType[
"Particle"]    = rnemdParticle;
 
  113    stringToFluxType[
"Particle+KE"] = rnemdParticleKE;
 
  114    stringToFluxType[
"KE+Px"]       = rnemdKePx;
 
  115    stringToFluxType[
"KE+Py"]       = rnemdKePy;
 
  116    stringToFluxType[
"KE+Pvector"]  = rnemdKePvector;
 
  117    stringToFluxType[
"KE+Lx"]       = rnemdKeLx;
 
  118    stringToFluxType[
"KE+Ly"]       = rnemdKeLy;
 
  119    stringToFluxType[
"KE+Lz"]       = rnemdKeLz;
 
  120    stringToFluxType[
"KE+Lvector"]  = rnemdKeLvector;
 
  122    if (rnemdParams->haveFluxType()) {
 
  123      rnemdFluxTypeLabel_ = rnemdParams->getFluxType();
 
  124      rnemdFluxType_      = stringToFluxType.find(rnemdFluxTypeLabel_)->second;
 
  126      std::string allowedFluxTypes;
 
  127      int currentLineLength = 0;
 
  129      for (std::map<std::string, RNEMDFluxType>::iterator fluxStrIter =
 
  130               stringToFluxType.begin();
 
  131           fluxStrIter != stringToFluxType.end(); ++fluxStrIter) {
 
  132        allowedFluxTypes += fluxStrIter->first + 
", ";
 
  133        currentLineLength += fluxStrIter->first.length() + 2;
 
  135        if (currentLineLength >= 50) {
 
  136          allowedFluxTypes += 
"\n\t\t";
 
  137          currentLineLength = 0;
 
  141      allowedFluxTypes.erase(allowedFluxTypes.length() - 2, 2);
 
  143      snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
  144               "RNEMD: No fluxType was set in the omd file. This parameter\n" 
  145               "\tmust be set to use RNEMD, and can take any of these values:\n" 
  147               allowedFluxTypes.c_str());
 
  148      painCave.isFatal  = 1;
 
  149      painCave.severity = OPENMD_ERROR;
 
  154    const std::string privAxis = rnemdParams->getPrivilegedAxis();
 
  156    if (privAxis == 
"x") {
 
  157      rnemdAxisLabel_      = 
"x";
 
  158      rnemdPrivilegedAxis_ = rnemdX;
 
  159    } 
else if (privAxis == 
"y") {
 
  160      rnemdAxisLabel_      = 
"y";
 
  161      rnemdPrivilegedAxis_ = rnemdY;
 
  163      rnemdAxisLabel_      = 
"z";
 
  164      rnemdPrivilegedAxis_ = rnemdZ;
 
  167    runTime_    = simParams->getRunTime();
 
  168    statusTime_ = simParams->getStatusTime();
 
  170    rnemdObjectSelection_ = rnemdParams->getObjectSelection();
 
  172    bool hasSlabWidth     = rnemdParams->haveSlabWidth();
 
  173    bool hasSlabACenter   = rnemdParams->haveSlabACenter();
 
  174    bool hasSlabBCenter   = rnemdParams->haveSlabBCenter();
 
  175    bool hasSphereARadius = rnemdParams->haveSphereARadius();
 
  176    bool hasSphereBRadius = rnemdParams->haveSphereBRadius();
 
  178    hasSelectionA_ = rnemdParams->haveSelectionA();
 
  179    hasSelectionB_ = rnemdParams->haveSelectionB();
 
  181    hasDividingArea_ = rnemdParams->haveDividingArea();
 
  182    dividingArea_    = rnemdParams->getDividingArea();
 
  184    bool hasCoordinateOrigin = rnemdParams->haveCoordinateOrigin();
 
  185    bool hasOutputFileName   = rnemdParams->haveOutputFileName();
 
  186    bool hasOutputFields     = rnemdParams->haveOutputFields();
 
  187    bool hasOutputSelection  = rnemdParams->haveOutputSelection();
 
  189    if (hasOutputSelection) {
 
  190      outputSelection_ = rnemdParams->getOutputSelection();
 
  192      outputSelection_ = rnemdObjectSelection_;
 
  195    if (hasCoordinateOrigin) {
 
  196      std::vector<RealType> co = rnemdParams->getCoordinateOrigin();
 
  197      if (co.size() != 3) {
 
  198        snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
  199                 "RNEMD: Incorrect number of parameters specified for " 
  200                 "coordinateOrigin.\n" 
  201                 "\tthere should be 3 parameters, but %zu were specified.\n",
 
  203        painCave.isFatal = 1;
 
  206      coordinateOrigin_.x() = co[0];
 
  207      coordinateOrigin_.y() = co[1];
 
  208      coordinateOrigin_.z() = co[2];
 
  210      coordinateOrigin_ = V3Zero;
 
  213    outputEvaluator_.loadScriptString(outputSelection_);
 
  214    outputSeleMan_.setSelectionSet(outputEvaluator_.evaluate());
 
  217        outputSeleMan_.replaceRigidBodiesWithAtoms();
 
  220    std::copy(osTypes.begin(), osTypes.end(), std::back_inserter(outputTypes_));
 
  222    nBins_    = rnemdParams->getOutputBins();
 
  223    binWidth_ = rnemdParams->getOutputBinWidth();
 
  226    data_.resize(RNEMD::ENDINDEX);
 
  229    z.units = 
"Angstroms";
 
  230    z.title = rnemdAxisLabel_;
 
  231    for (
unsigned int i = 0; i < nBins_; i++)
 
  232      z.accumulator.push_back(
 
  234    data_[Z]        = std::move(z);
 
  238    r.units = 
"Angstroms";
 
  240    for (
unsigned int i = 0; i < nBins_; i++)
 
  241      r.accumulator.push_back(
 
  243    data_[R]        = std::move(r);
 
  246    OutputData temperature;
 
  247    temperature.units = 
"K";
 
  248    temperature.title = 
"Temperature";
 
  249    for (
unsigned int i = 0; i < nBins_; i++)
 
  250      temperature.accumulator.push_back(
 
  252    data_[TEMPERATURE]        = std::move(temperature);
 
  253    outputMap_[
"TEMPERATURE"] = TEMPERATURE;
 
  256    velocity.units = 
"angstroms/fs";
 
  257    velocity.title = 
"Velocity";
 
  258    for (
unsigned int i = 0; i < nBins_; i++)
 
  259      velocity.accumulator.push_back(
 
  261    data_[VELOCITY]        = std::move(velocity);
 
  262    outputMap_[
"VELOCITY"] = VELOCITY;
 
  264    OutputData angularVelocity;
 
  265    angularVelocity.units = 
"angstroms^2/fs";
 
  266    angularVelocity.title = 
"AngularVelocity";
 
  267    for (
unsigned int i = 0; i < nBins_; i++)
 
  268      angularVelocity.accumulator.push_back(
 
  270    data_[ANGULARVELOCITY]        = std::move(angularVelocity);
 
  271    outputMap_[
"ANGULARVELOCITY"] = ANGULARVELOCITY;
 
  274    density.units = 
"g cm^-3";
 
  275    density.title = 
"Density";
 
  276    for (
unsigned int i = 0; i < nBins_; i++)
 
  277      density.accumulator.push_back(
 
  279    data_[DENSITY]        = std::move(density);
 
  280    outputMap_[
"DENSITY"] = DENSITY;
 
  283    activity.units = 
"unitless";
 
  284    activity.title = 
"Activity";
 
  285    for (
unsigned int i = 0; i < nBins_; i++)
 
  286      activity.accumulator.push_back(
 
  288    data_[ACTIVITY]        = std::move(activity);
 
  289    outputMap_[
"ACTIVITY"] = ACTIVITY;
 
  292    eField.units = 
"kcal/mol/angstroms/e";
 
  293    eField.title = 
"Electric Field";
 
  294    for (
unsigned int i = 0; i < nBins_; i++)
 
  295      eField.accumulator.push_back(
 
  297    data_[ELECTRICFIELD]        = std::move(eField);
 
  298    outputMap_[
"ELECTRICFIELD"] = ELECTRICFIELD;
 
  301    ePot.units = 
"kcal/mol/e";
 
  302    ePot.title = 
"Electrostatic Potential";
 
  303    for (
unsigned int i = 0; i < nBins_; i++)
 
  304      ePot.accumulator.push_back(
 
  306    data_[ELECTROSTATICPOTENTIAL]        = std::move(ePot);
 
  307    outputMap_[
"ELECTROSTATICPOTENTIAL"] = ELECTROSTATICPOTENTIAL;
 
  309    if (hasOutputFields) {
 
  310      parseOutputFileFormat(rnemdParams->getOutputFields());
 
  312      if (usePeriodicBoundaryConditions_)
 
  316      switch (rnemdFluxType_) {
 
  320        outputMask_.set(TEMPERATURE);
 
  324        outputMask_.set(VELOCITY);
 
  328        outputMask_.set(VELOCITY);
 
  329        outputMask_.set(DENSITY);
 
  335        outputMask_.set(ANGULARVELOCITY);
 
  341        outputMask_.set(TEMPERATURE);
 
  342        outputMask_.set(ANGULARVELOCITY);
 
  346        outputMask_.set(TEMPERATURE);
 
  347        outputMask_.set(VELOCITY);
 
  350        outputMask_.set(TEMPERATURE);
 
  351        outputMask_.set(VELOCITY);
 
  352        outputMask_.set(DENSITY);
 
  359    if (hasOutputFileName) {
 
  360      rnemdFileName_ = rnemdParams->getOutputFileName();
 
  362      rnemdFileName_ = 
getPrefix(info->getFinalConfigFileName()) + 
".rnemd";
 
  367    exchangeTime_  = rnemdParams->getExchangeTime();
 
  368    RealType dt    = simParams->getDt();
 
  369    RealType newET = std::ceil(exchangeTime_ / dt) * dt;
 
  371    if (std::fabs(newET - exchangeTime_) > 1e-6) {
 
  372      snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
  373               "RNEMD: The exchangeTime was reset to %lf,\n" 
  374               "\t\twhich is a multiple of dt, %lf.\n",
 
  376      painCave.isFatal  = 0;
 
  377      painCave.severity = OPENMD_WARNING;
 
  379      exchangeTime_ = newET;
 
  383    hmat_        = currentSnap_->
getHmat();
 
  386    std::ostringstream selectionAstream;
 
  387    std::ostringstream selectionBstream;
 
  389    if (hasSelectionA_) {
 
  390      selectionA_ = rnemdParams->getSelectionA();
 
  392      if (usePeriodicBoundaryConditions_) {
 
  395                rnemdParams->getSlabWidth() :
 
  396                hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) / 10.0;
 
  398        slabACenter_ = hasSlabACenter ? rnemdParams->getSlabACenter() : 0.0;
 
  400        selectionA_ = this->setSelection(slabACenter_);
 
  402        if (hasSphereARadius)
 
  403          sphereARadius_ = rnemdParams->getSphereARadius();
 
  408          RealType hVol = thermo.getHullVolume();
 
  410              0.1 * pow((3.0 * hVol / (4.0 * Constants::PI)), 1.0 / 3.0);
 
  412        selectionAstream << 
"select r < " << sphereARadius_;
 
  413        selectionA_ = selectionAstream.str();
 
  417    if (hasSelectionB_) {
 
  418      selectionB_ = rnemdParams->getSelectionB();
 
  420      if (usePeriodicBoundaryConditions_) {
 
  423                rnemdParams->getSlabWidth() :
 
  424                hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) / 10.0;
 
  428                rnemdParams->getSlabBCenter() :
 
  429                hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) / 2.0;
 
  431        selectionB_ = this->setSelection(slabBCenter_);
 
  433        if (hasSphereBRadius) {
 
  434          sphereBRadius_ = rnemdParams->getSphereBRadius();
 
  435          selectionBstream << 
"select r > " << sphereBRadius_;
 
  436          selectionB_ = selectionBstream.str();
 
  438          selectionB_    = 
"select hull";
 
  439          hasSelectionB_ = 
true;
 
  445    evaluator_.loadScriptString(rnemdObjectSelection_);
 
  446    if (!evaluator_.isDynamic())
 
  447      seleMan_.setSelectionSet(evaluator_.evaluate());
 
  449    evaluatorA_.loadScriptString(selectionA_);
 
  450    if (!evaluatorA_.isDynamic())
 
  451      seleManA_.setSelectionSet(evaluatorA_.evaluate());
 
  453    evaluatorB_.loadScriptString(selectionB_);
 
  454    if (!evaluatorB_.isDynamic())
 
  455      seleManB_.setSelectionSet(evaluatorB_.evaluate());
 
  459        seleMan_.removeAtomsInRigidBodies().getSelectionCount();
 
  462    if (selectionCount > nIntegrable) {
 
  463      snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
  464               "RNEMD: The current objectSelection,\n" 
  466               "\thas resulted in %d selected objects.  However,\n" 
  467               "\tthe total number of integrable objects in the system\n" 
  468               "\tis only %d.  This is almost certainly not what you want\n" 
  470               rnemdObjectSelection_.c_str(), selectionCount, nIntegrable);
 
  471      painCave.isFatal  = 0;
 
  472      painCave.severity = OPENMD_WARNING;
 
  478    if (!doRNEMD_) 
return;
 
  480    if (worldRank == 0) {
 
  490  void RNEMD::getStarted() {
 
  491    if (!doRNEMD_) 
return;
 
  496  void RNEMD::doRNEMD() {
 
  497    if (!doRNEMD_) 
return;
 
  499    hmat_ = currentSnap_->getHmat();
 
  502    evaluator_.loadScriptString(rnemdObjectSelection_);
 
  503    if (evaluator_.isDynamic()) seleMan_.setSelectionSet(evaluator_.evaluate());
 
  505    evaluatorA_.loadScriptString(selectionA_);
 
  506    if (evaluatorA_.isDynamic())
 
  507      seleManA_.setSelectionSet(evaluatorA_.evaluate());
 
  509    evaluatorB_.loadScriptString(selectionB_);
 
  510    if (evaluatorB_.isDynamic())
 
  511      seleManB_.setSelectionSet(evaluatorB_.evaluate());
 
  513    commonA_ = seleManA_ & seleMan_;
 
  514    commonB_ = seleManB_ & seleMan_;
 
  516    auto reducedCommonA = commonA_.removeAtomsInRigidBodies();
 
  517    auto reducedCommonB = commonB_.removeAtomsInRigidBodies();
 
  523    RealType area = getDefaultDividingArea();
 
  525    kineticTarget_         = kineticFlux_ * exchangeTime_ * area;
 
  526    momentumTarget_        = momentumFluxVector_ * exchangeTime_ * area;
 
  527    angularMomentumTarget_ = angularMomentumFluxVector_ * exchangeTime_ * area;
 
  528    particleTarget_        = particleFlux_ * exchangeTime_ * area;
 
  530    if (std::fabs(particleTarget_) > 1.0) {
 
  531      snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
  532               "RNEMD: The current particleFlux,\n" 
  534               "\thas resulted in a target particle exchange of %f.\n" 
  535               "\tThis is equivalent to moving more than one particle\n" 
  536               "\tduring each exchange.  Please reduce your particleFlux.\n",
 
  537               particleFlux_, particleTarget_);
 
  538      painCave.isFatal  = 1;
 
  539      painCave.severity = OPENMD_ERROR;
 
  543    if (rnemdFluxType_ == rnemdParticle || rnemdFluxType_ == rnemdParticleKE) {
 
  547      auto reducedTempCommonA = tempCommonA.removeAtomsInRigidBodies();
 
  548      auto reducedTempCommonB = tempCommonB.removeAtomsInRigidBodies();
 
  550      this->doRNEMDImpl(reducedTempCommonA, reducedTempCommonB);
 
  552      this->doRNEMDImpl(reducedCommonA, reducedCommonB);
 
  556  void RNEMD::collectData() {
 
  557    if (!doRNEMD_) 
return;
 
  558    currentSnap_ = info_->getSnapshotManager()->getCurrentSnapshot();
 
  559    hmat_        = currentSnap_->getHmat();
 
  563    RealType area = getDefaultDividingArea();
 
  564    areaAccumulator_.add(area);
 
  566    Vector3d u = angularMomentumFluxVector_;
 
  570    if (outputEvaluator_.isDynamic()) {
 
  571      outputSeleMan_.setSelectionSet(outputEvaluator_.evaluate());
 
  586    vector<RealType> binMass(nBins_, 0.0);
 
  587    vector<Vector3d> binP(nBins_, V3Zero);
 
  588    vector<RealType> binOmega(nBins_, 0.0);
 
  589    vector<Vector3d> binL(nBins_, V3Zero);
 
  590    vector<Mat3x3d> binI(nBins_);
 
  591    vector<RealType> binKE(nBins_, 0.0);
 
  592    vector<Vector3d> binEField(nBins_, V3Zero);
 
  593    vector<int> binDOF(nBins_, 0);
 
  594    vector<int> binCount(nBins_, 0);
 
  595    vector<int> binEFieldCount(nBins_, 0);
 
  596    vector<vector<int>> binTypeCounts;
 
  598    if (outputMask_[ACTIVITY]) {
 
  599      binTypeCounts.resize(nBins_);
 
  600      for (
unsigned int i = 0; i < nBins_; i++) {
 
  601        binTypeCounts[i].resize(outputTypes_.size(), 0);
 
  605    SimInfo::MoleculeIterator miter;
 
  606    std::vector<StuntDouble*>::iterator iiter;
 
  607    std::vector<AtomType*>::iterator at;
 
  612    Molecule::ConstraintPairIterator cpi;
 
  614    for (mol = info_->beginMolecule(miter); mol != NULL;
 
  615         mol = info_->nextMolecule(miter)) {
 
  616      for (sd = mol->beginIntegrableObject(iiter); sd != NULL;
 
  617           sd = mol->nextIntegrableObject(iiter)) {
 
  618        if (outputSeleMan_.isSelected(sd)) {
 
  624          rPos = sd->
getPos() - coordinateOrigin_;
 
  635              KE += 0.5 * (angMom[j] * angMom[j] / Ia(j, j) +
 
  636                           angMom[k] * angMom[k] / Ia(k, k));
 
  639              KE += 0.5 * (angMom[0] * angMom[0] / Ia(0, 0) +
 
  640                           angMom[1] * angMom[1] / Ia(1, 1) +
 
  641                           angMom[2] * angMom[2] / Ia(2, 2));
 
  646          L  = mass * 
cross(rPos, vel);
 
  647          I  = outProduct(rPos, rPos) * mass;
 
  648          r2 = rPos.lengthSquare();
 
  649          I(0, 0) += mass * r2;
 
  650          I(1, 1) += mass * r2;
 
  651          I(2, 2) += mass * r2;
 
  653          if (outputMask_[ACTIVITY]) {
 
  658              std::vector<Atom*>::iterator ai;
 
  660              for (atom = rb->beginAtom(ai); atom != NULL;
 
  661                   atom = rb->nextAtom(ai)) {
 
  662                atomBinNo = getBin(atom->
getPos());
 
  664                atype = 
static_cast<Atom*
>(atom)->getAtomType();
 
  665                at = std::find(outputTypes_.begin(), outputTypes_.end(), atype);
 
  666                if (at != outputTypes_.end()) {
 
  667                  typeIndex = std::distance(outputTypes_.begin(), at);
 
  670                if (atomBinNo >= 0 && atomBinNo < 
int(nBins_)) {
 
  671                  if (typeIndex != -1) binTypeCounts[atomBinNo][typeIndex]++;
 
  674            } 
else if (sd->
isAtom()) {
 
  675              atype = 
static_cast<Atom*
>(sd)->getAtomType();
 
  676              at = std::find(outputTypes_.begin(), outputTypes_.end(), atype);
 
  677              if (at != outputTypes_.end()) {
 
  678                typeIndex = std::distance(outputTypes_.begin(), at);
 
  683          if (binNo >= 0 && binNo < 
int(nBins_)) {
 
  685            binMass[binNo] += mass;
 
  686            binP[binNo] += mass * vel;
 
  690            binDOF[binNo] += DOF;
 
  692            if (outputMask_[ACTIVITY] && typeIndex != -1)
 
  693              binTypeCounts[binNo][typeIndex]++;
 
  699        if (outputMask_[ELECTRICFIELD]) {
 
  703            std::vector<Atom*>::iterator ai;
 
  705            for (atom = rb->beginAtom(ai); atom != NULL;
 
  706                 atom = rb->nextAtom(ai)) {
 
  707              atomBinNo = getBin(atom->
getPos());
 
  710              binEFieldCount[atomBinNo]++;
 
  711              binEField[atomBinNo] += eField;
 
  715            atomBinNo = getBin(sd->
getPos());
 
  717            binEFieldCount[atomBinNo]++;
 
  718            binEField[atomBinNo] += eField;
 
  725      if (outputSeleMan_.isSelected(mol)) {
 
  726        for (consPair = mol->beginConstraintPair(cpi); consPair != NULL;
 
  727             consPair = mol->nextConstraintPair(cpi)) {
 
  731          if (usePeriodicBoundaryConditions_) {
 
  732            currentSnap_->wrapVector(posA);
 
  733            currentSnap_->wrapVector(posB);
 
  737          int binCons  = getBin(coc);
 
  738          binDOF[binCons] -= 1;
 
  744    for (
unsigned int i = 0; i < nBins_; i++) {
 
  745      MPI_Allreduce(MPI_IN_PLACE, &binCount[i], 1, MPI_INT, MPI_SUM,
 
  747      MPI_Allreduce(MPI_IN_PLACE, &binMass[i], 1, MPI_REALTYPE, MPI_SUM,
 
  749      MPI_Allreduce(MPI_IN_PLACE, binP[i].getArrayPointer(), 3, MPI_REALTYPE,
 
  750                    MPI_SUM, MPI_COMM_WORLD);
 
  751      MPI_Allreduce(MPI_IN_PLACE, binL[i].getArrayPointer(), 3, MPI_REALTYPE,
 
  752                    MPI_SUM, MPI_COMM_WORLD);
 
  753      MPI_Allreduce(MPI_IN_PLACE, binI[i].getArrayPointer(), 9, MPI_REALTYPE,
 
  754                    MPI_SUM, MPI_COMM_WORLD);
 
  755      MPI_Allreduce(MPI_IN_PLACE, &binKE[i], 1, MPI_REALTYPE, MPI_SUM,
 
  757      MPI_Allreduce(MPI_IN_PLACE, &binDOF[i], 1, MPI_INT, MPI_SUM,
 
  759      MPI_Allreduce(MPI_IN_PLACE, &binEFieldCount[i], 1, MPI_INT, MPI_SUM,
 
  762      if (outputMask_[ELECTRICFIELD]) {
 
  763        MPI_Allreduce(MPI_IN_PLACE, binEField[i].getArrayPointer(), 3,
 
  764                      MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
 
  766      if (outputMask_[ACTIVITY]) {
 
  767        MPI_Allreduce(MPI_IN_PLACE, &binTypeCounts[i][0], outputTypes_.size(),
 
  768                      MPI_INT, MPI_SUM, MPI_COMM_WORLD);
 
  774    RealType z, r, temp, binVolume, den(0.0), dz(0.0);
 
  775    std::vector<RealType> nden(outputTypes_.size(), 0.0);
 
  776    RealType boxVolume = currentSnap_->getVolume();
 
  779    for (
unsigned int i = 0; i < nBins_; i++) {
 
  780      if (usePeriodicBoundaryConditions_) {
 
  781        z = (((RealType)i + 0.5) / (RealType)nBins_) *
 
  782            hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_);
 
  783        data_[Z].accumulator[i]->add(z);
 
  785        binVolume = boxVolume / nBins_;
 
  786        dz        = hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) /
 
  789        r = (((RealType)i + 0.5) * binWidth_);
 
  790        data_[R].accumulator[i]->add(r);
 
  792        RealType rinner = (RealType)i * binWidth_;
 
  793        RealType router = (RealType)(i + 1) * binWidth_;
 
  795            (4.0 * Constants::PI * (pow(router, 3) - pow(rinner, 3))) / 3.0;
 
  800      if (outputMask_[ELECTRICFIELD] && binEFieldCount[i] > 0) {
 
  801        eField = binEField[i] / RealType(binEFieldCount[i]);
 
  802        data_[ELECTRICFIELD].accumulator[i]->add(eField);
 
  805      if (outputMask_[ELECTROSTATICPOTENTIAL]) {
 
  806        if (usePeriodicBoundaryConditions_ && binEFieldCount[i] > 0) {
 
  807          ePot += eField[rnemdPrivilegedAxis_] * dz;
 
  808          data_[ELECTROSTATICPOTENTIAL].accumulator[i]->add(ePot);
 
  814      if (outputMask_[DENSITY]) {
 
  815        den = binMass[i] * Constants::densityConvert / binVolume;
 
  816        data_[DENSITY].accumulator[i]->add(den);
 
  819      if (outputMask_[ACTIVITY]) {
 
  820        for (
unsigned int j = 0; j < outputTypes_.size(); j++) {
 
  821          nden[j] = (binTypeCounts[i][j] / binVolume) *
 
  822                    Constants::concentrationConvert;
 
  824        data_[ACTIVITY].accumulator[i]->add(nden);
 
  827      if (binCount[i] > 0) {
 
  830        if (outputMask_[VELOCITY]) {
 
  831          vel = binP[i] / binMass[i];
 
  832          data_[VELOCITY].accumulator[i]->add(vel);
 
  835        if (outputMask_[ANGULARVELOCITY]) {
 
  836          omega = binI[i].inverse() * binL[i];
 
  837          data_[ANGULARVELOCITY].accumulator[i]->
add(omega);
 
  840        if (outputMask_[TEMPERATURE]) {
 
  842            temp = 2.0 * binKE[i] /
 
  843                   (binDOF[i] * Constants::kb * Constants::energyConvert);
 
  844            data_[TEMPERATURE].accumulator[i]->add(temp);
 
  846            std::cerr << 
"No degrees of freedom in this bin?\n";
 
  855  void RNEMD::writeOutputFile() {
 
  856    if (!doRNEMD_) 
return;
 
  857    if (!hasData_) 
return;
 
  862    MPI_Comm_rank(MPI_COMM_WORLD, &worldRank);
 
  864    if (worldRank == 0) {
 
  866      rnemdFile_.open(rnemdFileName_.c_str(), std::ios::out | std::ios::trunc);
 
  869        snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
  870                 "Could not open \"%s\" for RNEMD output.\n",
 
  871                 rnemdFileName_.c_str());
 
  872        painCave.isFatal = 1;
 
  876      RealType time    = currentSnap_->getTime();
 
  877      RealType avgArea = areaAccumulator_.getAverage();
 
  884      if (time >= info_->getSimParams()->getDt()) {
 
  885        Jz    = kineticExchange_ / (time * avgArea) / Constants::energyConvert;
 
  886        JzP   = momentumExchange_ / (time * avgArea);
 
  887        JzL   = angularMomentumExchange_ / (time * avgArea);
 
  888        Jpart = particleExchange_ / (time * avgArea);
 
  891      rnemdFile_ << 
"#######################################################\n";
 
  892      rnemdFile_ << 
"# RNEMD {\n";
 
  893      rnemdFile_ << 
"#    exchangeMethod  = \"" << rnemdMethodLabel_ << 
"\";\n";
 
  894      rnemdFile_ << 
"#    fluxType  = \"" << rnemdFluxTypeLabel_ << 
"\";\n";
 
  896      if (usePeriodicBoundaryConditions_)
 
  897        rnemdFile_ << 
"#    privilegedAxis = " << rnemdAxisLabel_ << 
";\n";
 
  899      rnemdFile_ << 
"#    exchangeTime = " << exchangeTime_ << 
";\n";
 
  900      rnemdFile_ << 
"#    objectSelection = \"" << rnemdObjectSelection_
 
  902      rnemdFile_ << 
"#    selectionA = \"" << selectionA_ << 
"\";\n";
 
  903      rnemdFile_ << 
"#    selectionB = \"" << selectionB_ << 
"\";\n";
 
  904      rnemdFile_ << 
"#    outputSelection = \"" << outputSelection_ << 
"\";\n";
 
  905      rnemdFile_ << 
"# }\n";
 
  906      rnemdFile_ << 
"#######################################################\n";
 
  907      rnemdFile_ << 
"# RNEMD report:\n";
 
  908      rnemdFile_ << 
"#      running time = " << time << 
" fs\n";
 
  909      rnemdFile_ << 
"# Target flux:\n";
 
  910      rnemdFile_ << 
"#           kinetic = " 
  911                 << kineticFlux_ / Constants::energyConvert
 
  912                 << 
" (kcal/mol/A^2/fs)\n";
 
  913      rnemdFile_ << 
"#          momentum = " << momentumFluxVector_
 
  914                 << 
" (amu/A/fs^2)\n";
 
  915      rnemdFile_ << 
"#  angular momentum = " << angularMomentumFluxVector_
 
  916                 << 
" (amu/A^2/fs^2)\n";
 
  917      rnemdFile_ << 
"#          particle = " << particleFlux_
 
  918                 << 
" (particles/A^2/fs)\n";
 
  920      rnemdFile_ << 
"# Target one-time exchanges:\n";
 
  921      rnemdFile_ << 
"#          kinetic = " 
  922                 << kineticTarget_ / Constants::energyConvert
 
  924      rnemdFile_ << 
"#          momentum = " << momentumTarget_
 
  926      rnemdFile_ << 
"#  angular momentum = " << angularMomentumTarget_
 
  927                 << 
" (amu*A^2/fs)\n";
 
  928      rnemdFile_ << 
"#          particle = " << particleTarget_
 
  930      rnemdFile_ << 
"# Actual exchange totals:\n";
 
  931      rnemdFile_ << 
"#          kinetic = " 
  932                 << kineticExchange_ / Constants::energyConvert
 
  934      rnemdFile_ << 
"#          momentum = " << momentumExchange_
 
  936      rnemdFile_ << 
"#  angular momentum = " << angularMomentumExchange_
 
  937                 << 
" (amu*A^2/fs)\n";
 
  938      rnemdFile_ << 
"#         particles = " << particleExchange_
 
  941      rnemdFile_ << 
"# Actual flux:\n";
 
  942      rnemdFile_ << 
"#          kinetic = " << Jz << 
" (kcal/mol/A^2/fs)\n";
 
  943      rnemdFile_ << 
"#          momentum = " << JzP << 
" (amu/A/fs^2)\n";
 
  944      rnemdFile_ << 
"#  angular momentum = " << JzL << 
" (amu/A^2/fs^2)\n";
 
  945      rnemdFile_ << 
"#          particle = " << Jpart
 
  946                 << 
" (particles/A^2/fs)\n";
 
  947      rnemdFile_ << 
"# Exchange statistics:\n";
 
  948      rnemdFile_ << 
"#               attempted = " << trialCount_ << 
"\n";
 
  949      rnemdFile_ << 
"#                  failed = " << failTrialCount_ << 
"\n";
 
  950      if (rnemdMethodLabel_ == 
"NIVS") {
 
  951        rnemdFile_ << 
"#  NIVS root-check errors = " << failRootCount_ << 
"\n";
 
  953      rnemdFile_ << 
"#######################################################\n";
 
  957      for (
unsigned int i = 0; i < outputMask_.size(); ++i) {
 
  958        if (outputMask_[i]) {
 
  959          rnemdFile_ << 
"\t" << data_[i].title << 
"(" << data_[i].units << 
")";
 
  962          if (data_[i].accumulator[0]->getType() ==
 
  963              std::type_index(
typeid(
Vector3d))) {
 
  964            rnemdFile_ << 
"\t\t";
 
  967          if (data_[i].accumulator[0]->getType() ==
 
  968              std::type_index(
typeid(std::vector<RealType>))) {
 
  970            for (
unsigned int type = 0; type < outputTypes_.size(); type++) {
 
  971              rnemdFile_ << outputTypes_[type]->getName() << 
"\t";
 
  978      rnemdFile_ << std::endl;
 
  980      std::vector<int> nonEmptyAccumulators(nBins_);
 
  981      int numberOfAccumulators {};
 
  983      for (
unsigned int i = 0; i < outputMask_.size(); ++i) {
 
  984        if (outputMask_[i]) {
 
  985          for (
unsigned int bin = 0; bin < nBins_; bin++) {
 
  986            nonEmptyAccumulators[bin] +=
 
  987                static_cast<int>(data_[i].accumulator[bin]->getCount() != 0);
 
  990          numberOfAccumulators++;
 
  994      rnemdFile_.precision(8);
 
  996      for (
unsigned int bin = 0; bin < nBins_; bin++) {
 
  997        if (nonEmptyAccumulators[bin] == numberOfAccumulators) {
 
  998          for (
unsigned int i = 0; i < outputMask_.size(); ++i) {
 
  999            if (outputMask_[i]) {
 
 1000              std::string message =
 
 1001                  "RNEMD detected a numerical error writing: " +
 
 1002                  data_[i].title + 
" for bin " + std::to_string(bin);
 
 1004              data_[i].accumulator[bin]->writeData(rnemdFile_, message);
 
 1008          rnemdFile_ << std::endl;
 
 1012      rnemdFile_ << 
"#######################################################\n";
 
 1013      rnemdFile_ << 
"# 95% confidence intervals in those quantities follow:\n";
 
 1014      rnemdFile_ << 
"#######################################################\n";
 
 1016      for (
unsigned int bin = 0; bin < nBins_; bin++) {
 
 1017        if (nonEmptyAccumulators[bin] == numberOfAccumulators) {
 
 1020          for (
unsigned int i = 0; i < outputMask_.size(); ++i) {
 
 1021            if (outputMask_[i]) {
 
 1022              std::string message =
 
 1023                  "RNEMD detected a numerical error writing: " +
 
 1024                  data_[i].title + 
" std. dev. for bin " + std::to_string(bin);
 
 1026              data_[i].accumulator[bin]->writeErrorBars(rnemdFile_, message);
 
 1030          rnemdFile_ << std::endl;
 
 1041  void RNEMD::setKineticFlux(RealType kineticFlux) {
 
 1044    kineticFlux_ = kineticFlux * Constants::energyConvert;
 
 1047  void RNEMD::setParticleFlux(RealType particleFlux) {
 
 1048    particleFlux_ = particleFlux;
 
 1051  void RNEMD::setMomentumFluxVector(
 
 1052      const std::vector<RealType>& momentumFluxVector) {
 
 1053    if (momentumFluxVector.size() != 3) {
 
 1054      snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
 1055               "RNEMD: Incorrect number of parameters specified for " 
 1056               "momentumFluxVector.\n" 
 1057               "\tthere should be 3 parameters, but %zu were specified.\n",
 
 1058               momentumFluxVector.size());
 
 1059      painCave.isFatal = 1;
 
 1063    momentumFluxVector_.x() = momentumFluxVector[0];
 
 1064    momentumFluxVector_.y() = momentumFluxVector[1];
 
 1065    momentumFluxVector_.z() = momentumFluxVector[2];
 
 1068  void RNEMD::setAngularMomentumFluxVector(
 
 1069      const std::vector<RealType>& angularMomentumFluxVector) {
 
 1070    if (angularMomentumFluxVector.size() != 3) {
 
 1071      snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
 1072               "RNEMD: Incorrect number of parameters specified for " 
 1073               "angularMomentumFluxVector.\n" 
 1074               "\tthere should be 3 parameters, but %zu were specified.\n",
 
 1075               angularMomentumFluxVector.size());
 
 1076      painCave.isFatal = 1;
 
 1080    angularMomentumFluxVector_.x() = angularMomentumFluxVector[0];
 
 1081    angularMomentumFluxVector_.y() = angularMomentumFluxVector[1];
 
 1082    angularMomentumFluxVector_.z() = angularMomentumFluxVector[2];
 
 1085  void RNEMD::parseOutputFileFormat(
const std::string& format) {
 
 1086    if (!doRNEMD_) 
return;
 
 1089    while (tokenizer.hasMoreTokens()) {
 
 1090      std::string token(tokenizer.nextToken());
 
 1092      OutputMapType::iterator i = outputMap_.find(token);
 
 1093      if (i != outputMap_.end()) {
 
 1094        outputMask_.set(i->second);
 
 1096        snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
 1097                 "RNEMD::parseOutputFileFormat: %s is not a recognized\n" 
 1098                 "\toutputFileFormat keyword.\n",
 
 1100        painCave.isFatal  = 0;
 
 1101        painCave.severity = OPENMD_ERROR;
 
 1107  std::string RNEMD::setSelection(RealType& slabCenter) {
 
 1108    bool printSlabCenterWarning {
false};
 
 1111    tempSlabCenter[rnemdPrivilegedAxis_] = slabCenter;
 
 1113    RealType hmat_2 = hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) / 2.0;
 
 1115    if (slabCenter > hmat_2) {
 
 1116      currentSnap_->wrapVector(tempSlabCenter);
 
 1117      printSlabCenterWarning = 
true;
 
 1118    } 
else if (slabCenter < -hmat_2) {
 
 1119      currentSnap_->wrapVector(tempSlabCenter);
 
 1120      printSlabCenterWarning = 
true;
 
 1123    if (printSlabCenterWarning) {
 
 1124      snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
 1125               "The given slab center was set to %0.2f. In the wrapped " 
 1127               "\t[-Hmat/2, +Hmat/2], this has been remapped to %0.2f.\n",
 
 1128               slabCenter, tempSlabCenter[rnemdPrivilegedAxis_]);
 
 1129      painCave.isFatal  = 0;
 
 1130      painCave.severity = OPENMD_WARNING;
 
 1133      slabCenter = tempSlabCenter[rnemdPrivilegedAxis_];
 
 1137    const RealType& leftSlabBoundary = leftSlab[rnemdPrivilegedAxis_];
 
 1138    leftSlab[rnemdPrivilegedAxis_]   = slabCenter - 0.5 * slabWidth_;
 
 1139    currentSnap_->wrapVector(leftSlab);
 
 1142    const RealType& rightSlabBoundary = rightSlab[rnemdPrivilegedAxis_];
 
 1143    rightSlab[rnemdPrivilegedAxis_]   = slabCenter + 0.5 * slabWidth_;
 
 1144    currentSnap_->wrapVector(rightSlab);
 
 1146    std::ostringstream selectionStream;
 
 1148    selectionStream << 
"select wrapped" << rnemdAxisLabel_
 
 1149                    << 
" >= " << leftSlabBoundary;
 
 1151    if (leftSlabBoundary > rightSlabBoundary)
 
 1152      selectionStream << 
" || wrapped" << rnemdAxisLabel_ << 
" < " 
 1153                      << rightSlabBoundary;
 
 1155      selectionStream << 
" && wrapped" << rnemdAxisLabel_ << 
" < " 
 1156                      << rightSlabBoundary;
 
 1158    return selectionStream.str();
 
 1161  RealType RNEMD::getDefaultDividingArea() {
 
 1162    if (hasDividingArea_) 
return dividingArea_;
 
 1164    Snapshot* snap = info_->getSnapshotManager()->getCurrentSnapshot();
 
 1166    if (hasSelectionA_) {
 
 1167      if (evaluatorA_.hasSurfaceArea()) {
 
 1168        areaA_   = evaluatorA_.getSurfaceArea();
 
 1169        volumeA_ = evaluatorA_.getVolume();
 
 1173        std::vector<StuntDouble*> aSites;
 
 1174        seleManA_.setSelectionSet(evaluatorA_.evaluate());
 
 1175        for (sd = seleManA_.beginSelected(isd); sd != NULL;
 
 1176             sd = seleManA_.nextSelected(isd)) {
 
 1177          aSites.push_back(sd);
 
 1179#if defined(HAVE_QHULL) 
 1181        surfaceMeshA->computeHull(aSites);
 
 1182        areaA_   = surfaceMeshA->getArea();
 
 1183        volumeA_ = surfaceMeshA->getVolume();
 
 1184        delete surfaceMeshA;
 
 1187            painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
 1188            "RNEMD::getDividingArea : Hull calculation is not possible\n" 
 1189            "\twithout libqhull. Please rebuild OpenMD with qhull enabled.");
 
 1190        painCave.severity = OPENMD_ERROR;
 
 1191        painCave.isFatal  = 1;
 
 1196      if (usePeriodicBoundaryConditions_) {
 
 1199        switch (rnemdPrivilegedAxis_) {
 
 1201          areaA_ = 2.0 * snap->getYZarea();
 
 1204          areaA_ = 2.0 * snap->getXZarea();
 
 1208          areaA_ = 2.0 * snap->getXYarea();
 
 1211        volumeA_ = areaA_ * slabWidth_;
 
 1216        areaA_   = 4.0 * Constants::PI * std::pow(sphereARadius_, 2);
 
 1217        volumeA_ = 4.0 * Constants::PI * std::pow(sphereARadius_, 3) / 3.0;
 
 1221    if (hasSelectionB_) {
 
 1222      if (evaluatorB_.hasSurfaceArea()) {
 
 1223        areaB_   = evaluatorB_.getSurfaceArea();
 
 1224        volumeB_ = evaluatorB_.getVolume();
 
 1228        std::vector<StuntDouble*> bSites;
 
 1229        seleManB_.setSelectionSet(evaluatorB_.evaluate());
 
 1230        for (sd = seleManB_.beginSelected(isd); sd != NULL;
 
 1231             sd = seleManB_.nextSelected(isd)) {
 
 1232          bSites.push_back(sd);
 
 1235#if defined(HAVE_QHULL) 
 1237        surfaceMeshB->computeHull(bSites);
 
 1238        areaB_   = surfaceMeshB->getArea();
 
 1239        volumeB_ = surfaceMeshB->getVolume();
 
 1240        delete surfaceMeshB;
 
 1243            painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
 
 1244            "RNEMD::getDividingArea : Hull calculation is not possible\n" 
 1245            "\twithout libqhull. Please rebuild OpenMD with qhull enabled.");
 
 1246        painCave.severity = OPENMD_ERROR;
 
 1247        painCave.isFatal  = 1;
 
 1252      if (usePeriodicBoundaryConditions_) {
 
 1255        switch (rnemdPrivilegedAxis_) {
 
 1257          areaB_ = 2.0 * snap->getYZarea();
 
 1260          areaB_ = 2.0 * snap->getXZarea();
 
 1264          areaB_ = 2.0 * snap->getXYarea();
 
 1267        volumeB_ = areaB_ * slabWidth_;
 
 1271        areaB_ = 4.0 * Constants::PI * pow(sphereBRadius_, 2);
 
 1273        RealType hVol = thermo.getHullVolume();
 
 1274        volumeB_ = hVol - 4.0 * Constants::PI * pow(sphereBRadius_, 3) / 3.0;
 
 1278    dividingArea_    = min(areaA_, areaB_);
 
 1279    hasDividingArea_ = 
true;
 
 1280    return dividingArea_;
 
 1284    if (usePeriodicBoundaryConditions_) {
 
 1285      currentSnap_->wrapVector(pos);
 
 1288                 (pos[rnemdPrivilegedAxis_] /
 
 1289                      hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) +
 
 1293      Vector3d rPos = pos - coordinateOrigin_;
 
 1294      return int(rPos.
length() / binWidth_);
 
AtomType is what OpenMD looks to for unchanging data about an atom.
Vector3d getPos()
Returns the current position of this stuntdouble.
ConstraintElem * getConsElem1()
Return the first constraint elemet.
ConstraintElem * getConsElem2()
Retunr the second constraint element.
ForceManager is responsible for calculating both the short range (bonded) interactions and long range...
AtomTypeSet getSelectedAtomTypes()
getSelectedAtomTypes
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
int getNGlobalIntegrableObjects()
Returns the total number of integrable objects (total number of rigid bodies plus the total number of...
SnapshotManager * getSnapshotManager()
Returns the snapshot manager.
The Snapshot class is a repository storing dynamic data during a Simulation.
Mat3x3d getHmat()
Returns the H-Matrix.
Snapshot * getCurrentSnapshot()
Returns the pointer of current snapshot.
The string tokenizer class allows an application to break a string into tokens The set of delimiters ...
"Don't move, or you're dead! Stand up! Captain, we've got them!"
Vector3d getVel()
Returns the current velocity of this stuntDouble.
int linearAxis()
Returns the linear axis of the rigidbody, atom and directional atom will always return -1.
RealType getMass()
Returns the mass of this stuntDouble.
virtual Mat3x3d getI()=0
Returns the inertia tensor of this stuntDouble.
bool isLinear()
Tests the if this stuntDouble is a linear rigidbody.
Vector3d getPos()
Returns the current position of this stuntDouble.
Vector3d getElectricField()
Returns the current electric field of this stuntDouble.
bool isRigidBody()
Tests if this stuntDouble is a rigid body.
Vector3d getJ()
Returns the current angular momentum of this stuntDouble (body -fixed).
bool isAtom()
Tests if this stuntDouble is an atom.
bool isDirectional()
Tests if this stuntDouble is a directional one.
Real length()
Returns the length of this vector.
void normalize()
Normalizes this vector in place.
Real lengthSquare()
Returns the squared length of this vector.
void add(const Vector< Real, Dim > &v1)
Sets the value of this vector to the sum of itself and v1 (*this += v1).
Vector3< Real > cross(const Vector3< Real > &v1, const Vector3< Real > &v2)
Returns the cross product of two Vectors.
std::string getPrefix(const std::string &str)