48#include "applications/staticProps/RNEMDStats.hpp"
59#include "applications/staticProps/SpatialStatistics.hpp"
60#include "brains/DataStorage.hpp"
62#include "io/Globals.hpp"
69#include "rnemd/RNEMDParameters.hpp"
70#include "types/AtomType.hpp"
71#include "types/FixedChargeAdapter.hpp"
72#include "types/FluctuatingChargeAdapter.hpp"
73#include "utils/Accumulator.hpp"
74#include "utils/AccumulatorView.hpp"
75#include "utils/BaseAccumulator.hpp"
76#include "utils/Constants.hpp"
79using namespace OpenMD::Utils;
83 RNEMDZ::RNEMDZ(
SimInfo* info,
const std::string& filename,
84 const std::string& sele,
int nzbins,
int axis) :
86 setOutputName(
getPrefix(filename) +
".rnemdZ");
88 evaluator_.loadScriptString(sele);
89 seleMan_.setSelectionSet(evaluator_.evaluate());
94 std::copy(osTypes.begin(), osTypes.end(), std::back_inserter(outputTypes_));
95 bool usePeriodicBoundaryConditions_ =
96 info_->getSimParams()->getUsePeriodicBoundaryConditions();
98 data_.resize(RNEMDZ::ENDINDEX);
101 z.units =
"Angstroms";
102 z.title = axisLabel_;
103 z.dataHandling = DataHandling::Average;
104 for (
unsigned int i = 0; i < nBins_; i++)
105 z.accumulator.push_back(
107 data_[Z] = std::move(z);
109 OutputData temperature;
110 temperature.units =
"K";
111 temperature.title =
"Temperature";
112 temperature.dataHandling = DataHandling::Average;
113 for (
unsigned int i = 0; i < nBins_; i++)
114 temperature.accumulator.push_back(
116 data_[TEMPERATURE] = std::move(temperature);
119 velocity.units =
"angstroms/fs";
120 velocity.title =
"Velocity";
121 velocity.dataHandling = DataHandling::Average;
122 for (
unsigned int i = 0; i < nBins_; i++)
123 velocity.accumulator.push_back(
125 data_[VELOCITY] = std::move(velocity);
128 density.units =
"g cm^-3";
129 density.title =
"Density";
130 density.dataHandling = DataHandling::Average;
131 for (
unsigned int i = 0; i < nBins_; i++)
132 density.accumulator.push_back(
134 data_[DENSITY] = std::move(density);
137 activity.units =
"unitless";
138 activity.title =
"Activity";
139 activity.dataHandling = DataHandling::Average;
140 unsigned int nTypes = outputTypes_.size();
143 for (
unsigned int i = 0; i < nBins_; i++)
144 activity.accumulator.push_back(
146 data_[ACTIVITY] = std::move(activity);
150 eField.units =
"kcal/mol/angstroms/e";
151 eField.title =
"Electric Field";
152 eField.dataHandling = DataHandling::Average;
153 for (
unsigned int i = 0; i < nBins_; i++)
154 eField.accumulator.push_back(
158 ePot.units =
"kcal/mol/e";
159 ePot.title =
"Electrostatic Potential";
160 ePot.dataHandling = DataHandling::Average;
161 for (
unsigned int i = 0; i < nBins_; i++)
162 ePot.accumulator.push_back(
167 charge.title =
"Charge";
168 charge.dataHandling = DataHandling::Average;
169 for (
unsigned int i = 0; i < nBins_; i++)
170 charge.accumulator.push_back(
173 OutputData chargeVelocity;
174 chargeVelocity.units =
"e/fs";
175 chargeVelocity.title =
"Charge_Velocity";
176 chargeVelocity.dataHandling = DataHandling::Average;
177 for (
unsigned int i = 0; i < nBins_; i++)
178 chargeVelocity.accumulator.push_back(
182 outputMask_.set(TEMPERATURE);
183 outputMask_.set(VELOCITY);
184 outputMask_.set(DENSITY);
185 outputMask_.set(ACTIVITY);
187 int atomStorageLayout = info_->getAtomStorageLayout();
188 int rigidBodyStorageLayout = info->getRigidBodyStorageLayout();
189 int cutoffGroupStorageLayout = info->getCutoffGroupStorageLayout();
191 if (atomStorageLayout & DataStorage::dslElectricField) {
192 outputMask_.set(ELECTRICFIELD);
193 outputMask_.set(ELECTROSTATICPOTENTIAL);
195 data_[ELECTRICFIELD] = std::move(eField);
196 data_[ELECTROSTATICPOTENTIAL] = std::move(ePot);
199 if (info_->usesElectrostaticAtoms() ||
200 atomStorageLayout & DataStorage::dslFlucQPosition) {
201 outputMask_.set(CHARGE);
203 data_[CHARGE] = std::move(charge);
206 if (atomStorageLayout & DataStorage::dslFlucQVelocity) {
207 outputMask_.set(CHARGEVELOCITY);
209 data_[CHARGEVELOCITY] = std::move(chargeVelocity);
213 void RNEMDZ::processFrame(
int istep) {
214 SlabStatistics::processFrame(istep);
216 if (evaluator_.isDynamic()) {
217 seleMan_.setSelectionSet(evaluator_.evaluate());
220 auto reducedSeleMan = seleMan_.removeAtomsInRigidBodies();
232 std::vector<RealType> binMass(nBins_, 0.0);
233 std::vector<Vector3d> binP(nBins_, V3Zero);
234 std::vector<RealType> binCharge(nBins_, 0.0);
235 std::vector<RealType> binChargeVelocity(nBins_, 0.0);
236 std::vector<RealType> binKE(nBins_, 0.0);
237 std::vector<Vector3d> binEField(nBins_, V3Zero);
238 std::vector<int> binDOF(nBins_, 0);
239 std::vector<int> binCount(nBins_, 0);
240 std::vector<std::vector<int>> binTypeCounts;
241 std::vector<int> binEFieldCount(nBins_, 0);
243 if (outputMask_[ACTIVITY]) {
244 binTypeCounts.resize(nBins_);
245 for (
unsigned int i = 0; i < nBins_; i++) {
246 binTypeCounts[i].resize(outputTypes_.size(), 0);
250 SimInfo::MoleculeIterator miter;
251 std::vector<StuntDouble*>::iterator iiter;
252 std::vector<AtomType*>::iterator at;
257 Molecule::ConstraintPairIterator cpi;
259 for (mol = info_->beginMolecule(miter); mol != NULL;
260 mol = info_->nextMolecule(miter)) {
261 for (sd = mol->beginIntegrableObject(iiter); sd != NULL;
262 sd = mol->nextIntegrableObject(iiter)) {
263 if (reducedSeleMan.isSelected(sd)) {
264 Vector3d pos = sd->
getPos();
273 Vector3d angMom = sd->
getJ();
274 Mat3x3d Ia = sd->
getI();
279 KE += 0.5 * (angMom[j] * angMom[j] / Ia(j, j) +
280 angMom[k] * angMom[k] / Ia(k, k));
283 KE += 0.5 * (angMom[0] * angMom[0] / Ia(0, 0) +
284 angMom[1] * angMom[1] / Ia(1, 1) +
285 angMom[2] * angMom[2] / Ia(2, 2));
290 if (outputMask_[ACTIVITY]) {
295 std::vector<Atom*>::iterator ai;
297 for (atom = rb->beginAtom(ai); atom != NULL;
298 atom = rb->nextAtom(ai)) {
299 atomBinNo = getBin(atom->
getPos());
301 atype =
static_cast<Atom*
>(atom)->getAtomType();
302 at = std::find(outputTypes_.begin(), outputTypes_.end(), atype);
303 if (at != outputTypes_.end()) {
304 typeIndex = std::distance(outputTypes_.begin(), at);
307 if (atomBinNo >= 0 && atomBinNo <
int(nBins_)) {
308 if (typeIndex != -1) binTypeCounts[atomBinNo][typeIndex]++;
311 }
else if (sd->
isAtom()) {
312 atype =
static_cast<Atom*
>(sd)->getAtomType();
313 at = std::find(outputTypes_.begin(), outputTypes_.end(), atype);
314 if (at != outputTypes_.end()) {
315 typeIndex = std::distance(outputTypes_.begin(), at);
320 if (binNo >= 0 && binNo <
int(nBins_)) {
322 binMass[binNo] += mass;
323 binP[binNo] += mass * vel;
325 binDOF[binNo] += DOF;
327 if (outputMask_[ACTIVITY] && typeIndex != -1)
328 binTypeCounts[binNo][typeIndex]++;
330 if (outputMask_[CHARGE] || outputMask_[CHARGEVELOCITY]) {
332 AtomType* atomType =
static_cast<Atom*
>(sd)->getAtomType();
334 if (fca.isFixedCharge()) { q = fca.getCharge(); }
337 if (fqa.isFluctuatingCharge()) {
342 if (outputMask_[CHARGE]) binCharge[binNo] += q;
343 if (outputMask_[CHARGEVELOCITY]) binChargeVelocity[binNo] += w;
346 std::vector<Atom*>::iterator ai;
348 for (atom = rb->beginAtom(ai); atom != NULL;
349 atom = rb->nextAtom(ai)) {
350 binNo = getBin(atom->
getPos());
353 if (fca.isFixedCharge()) { q = fca.getCharge(); }
357 if (fqa.isFluctuatingCharge()) {
362 if (outputMask_[CHARGE]) binCharge[binNo] += q;
363 if (outputMask_[CHARGEVELOCITY])
364 binChargeVelocity[binNo] += w;
373 if (outputMask_[ELECTRICFIELD]) {
376 std::vector<Atom*>::iterator ai;
378 for (atom = rb->beginAtom(ai); atom != NULL;
379 atom = rb->nextAtom(ai)) {
380 binNo = getBin(atom->
getPos());
382 binEFieldCount[binNo]++;
383 binEField[binNo] += eField;
387 binNo = getBin(sd->
getPos());
389 binEFieldCount[binNo]++;
390 binEField[binNo] += eField;
394 if (reducedSeleMan.isSelected(mol)) {
395 for (consPair = mol->beginConstraintPair(cpi); consPair != NULL;
396 consPair = mol->nextConstraintPair(cpi)) {
400 if (usePeriodicBoundaryConditions_) {
401 currentSnapshot_->wrapVector(posA);
402 currentSnapshot_->wrapVector(posB);
405 Vector3d coc = 0.5 * (posA + posB);
406 int binCons = getBin(coc);
407 binDOF[binCons] -= 1;
412 for (
unsigned int i = 0; i < nBins_; i++) {
413 RealType temp(0.0), ePot(0.0);
414 Vector3d vel(0.0), eField(0.0);
415 RealType z, den(0.0), binVolume(0.0), dz(0.0);
416 std::vector<RealType> nden(outputTypes_.size(), 0.0);
418 z = (((RealType)i + 0.5) / (RealType)nBins_) * hmat_(axis_, axis_);
419 data_[Z].accumulator[i]->add(z);
421 binVolume = volume_ / nBins_;
422 dz = hmat_(axis_, axis_) / (RealType)nBins_;
426 if (outputMask_[ELECTRICFIELD] && binEFieldCount[i] > 0) {
427 eField = binEField[i] / RealType(binEFieldCount[i]);
428 data_[ELECTRICFIELD].accumulator[i]->add(eField);
431 if (outputMask_[ELECTROSTATICPOTENTIAL] && binEFieldCount[i] > 0) {
432 ePot += eField[axis_] * dz;
433 data_[ELECTROSTATICPOTENTIAL].accumulator[i]->
add(ePot);
438 if (outputMask_[DENSITY]) {
439 den = binMass[i] * Constants::densityConvert / binVolume;
440 data_[DENSITY].accumulator[i]->add(den);
443 if (outputMask_[ACTIVITY]) {
444 for (
unsigned int j = 0; j < outputTypes_.size(); j++) {
445 nden[j] = (binTypeCounts[i][j] / binVolume) *
446 Constants::concentrationConvert;
448 data_[ACTIVITY].accumulator[i]->add(nden);
451 if (binCount[i] > 0) {
454 if (outputMask_[VELOCITY]) {
455 vel = binP[i] / binMass[i];
456 data_[VELOCITY].accumulator[i]->add(vel);
459 if (outputMask_[TEMPERATURE]) {
461 temp = 2.0 * binKE[i] /
462 (binDOF[i] * Constants::kb * Constants::energyConvert);
463 data_[TEMPERATURE].accumulator[i]->add(temp);
465 std::cerr <<
"No degrees of freedom in this bin?\n";
469 if (outputMask_[CHARGE])
470 data_[CHARGE].accumulator[i]->add(binCharge[i]);
472 if (outputMask_[CHARGEVELOCITY])
473 data_[CHARGEVELOCITY].accumulator[i]->add(binChargeVelocity[i]);
478 RNEMDR::RNEMDR(
SimInfo* info,
const std::string& filename,
479 const std::string& sele,
const std::string& comsele,
480 int nrbins, RealType binWidth) :
482 setOutputName(
getPrefix(filename) +
".rnemdR");
485 data_.resize(RNEMDR::ENDINDEX);
488 r.units =
"Angstroms";
490 r.dataHandling = DataHandling::Average;
491 for (
int i = 0; i < nBins_; i++)
492 r.accumulator.push_back(
494 data_[R] = std::move(r);
496 OutputData temperature;
497 temperature.units =
"K";
498 temperature.title =
"Temperature";
499 temperature.dataHandling = DataHandling::Average;
500 for (
unsigned int i = 0; i < nBins_; i++)
501 temperature.accumulator.push_back(
503 data_[TEMPERATURE] = std::move(temperature);
505 OutputData angularVelocity;
506 angularVelocity.units =
"angstroms/fs";
507 angularVelocity.title =
"Velocity";
508 angularVelocity.dataHandling = DataHandling::Average;
509 for (
unsigned int i = 0; i < nBins_; i++)
510 angularVelocity.accumulator.push_back(
512 data_[ANGULARVELOCITY] = std::move(angularVelocity);
515 density.units =
"g cm^-3";
516 density.title =
"Density";
517 density.dataHandling = DataHandling::Average;
518 for (
unsigned int i = 0; i < nBins_; i++)
519 density.accumulator.push_back(
521 data_[DENSITY] = std::move(density);
524 outputMask_.set(TEMPERATURE);
525 outputMask_.set(ANGULARVELOCITY);
526 outputMask_.set(DENSITY);
529 void RNEMDR::processFrame(
int istep) {
530 ShellStatistics::processFrame(istep);
532 if (evaluator_.isDynamic()) {
533 seleMan_.setSelectionSet(evaluator_.evaluate());
546 std::vector<int> binCount(nBins_, 0);
547 std::vector<RealType> binMass(nBins_, 0.0);
548 std::vector<Vector3d> binP(nBins_, V3Zero);
549 std::vector<RealType> binOmega(nBins_, 0.0);
550 std::vector<Vector3d> binL(nBins_, V3Zero);
551 std::vector<Mat3x3d> binI(nBins_);
552 std::vector<RealType> binKE(nBins_, 0.0);
553 std::vector<int> binDOF(nBins_, 0);
555 SimInfo::MoleculeIterator miter;
556 std::vector<StuntDouble*>::iterator iiter;
557 std::vector<AtomType*>::iterator at;
561 Molecule::ConstraintPairIterator cpi;
564 for (mol = info_->beginMolecule(miter); mol != NULL;
565 mol = info_->nextMolecule(miter)) {
566 for (sd = mol->beginIntegrableObject(iiter); sd != NULL;
567 sd = mol->nextIntegrableObject(iiter)) {
568 if (seleMan_.isSelected(sd)) {
570 binNo = getBin(sd->
getPos());
572 if (binNo >= 0 && binNo <
int(nBins_)) {
575 rPos = sd->
getPos() - coordinateOrigin_;
580 Vector3d angMom = sd->
getJ();
581 Mat3x3d Ia = sd->
getI();
586 KE += 0.5 * (angMom[j] * angMom[j] / Ia(j, j) +
587 angMom[k] * angMom[k] / Ia(k, k));
590 KE += 0.5 * (angMom[0] * angMom[0] / Ia(0, 0) +
591 angMom[1] * angMom[1] / Ia(1, 1) +
592 angMom[2] * angMom[2] / Ia(2, 2));
597 L = mass *
cross(rPos, vel);
598 I = outProduct(rPos, rPos) * mass;
600 I(0, 0) += mass * r2;
601 I(1, 1) += mass * r2;
602 I(2, 2) += mass * r2;
605 binMass[binNo] += mass;
606 binP[binNo] += mass * vel;
610 binDOF[binNo] += DOF;
614 if (seleMan_.isSelected(mol)) {
615 for (consPair = mol->beginConstraintPair(cpi); consPair != NULL;
616 consPair = mol->nextConstraintPair(cpi)) {
620 Vector3d coc = 0.5 * (posA + posB);
621 int binCons = getBin(coc);
622 if (binCons >= 0 && binCons <
int(nBins_)) { binDOF[binCons] -= 1; }
627 for (
unsigned int i = 0; i < nBins_; i++) {
628 RealType r, rinner, router, den(0.0), binVolume(0.0), temp(0.0);
631 r = (((RealType)i + 0.5) * binWidth_);
632 rinner = (RealType)i * binWidth_;
633 router = (RealType)(i + 1) * binWidth_;
635 (4.0 * Constants::PI * (pow(router, 3) - pow(rinner, 3))) / 3.0;
637 data_[R].accumulator[i]->add(r);
641 den = binMass[i] * Constants::densityConvert / binVolume;
642 data_[DENSITY].accumulator[i]->add(den);
647 omega = binI[i].inverse() * binL[i];
648 data_[ANGULARVELOCITY].accumulator[i]->add(omega);
650 temp = 2.0 * binKE[i] /
651 (binDOF[i] * Constants::kb * Constants::energyConvert);
652 data_[TEMPERATURE].accumulator[i]->add(temp);
657 RNEMDRTheta::RNEMDRTheta(
SimInfo* info,
const std::string& filename,
658 const std::string& sele,
const std::string& comsele,
659 int nrbins, RealType binWidth,
int nangleBins) :
661 nAngleBins_(nangleBins) {
662 Globals* simParams = info->getSimParams();
664 bool hasAngularMomentumFluxVector =
665 rnemdParams->haveAngularMomentumFluxVector();
667 if (hasAngularMomentumFluxVector) {
668 std::vector<RealType> amf = rnemdParams->getAngularMomentumFluxVector();
670 if (amf.size() != 3) {
671 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
672 "RNEMDRTheta: Incorrect number of parameters specified for "
673 "angularMomentumFluxVector.\n"
674 "\tthere should be 3 parameters, but %zu were specified.\n",
676 painCave.isFatal = 1;
679 fluxVector_.x() = amf[0];
680 fluxVector_.y() = amf[1];
681 fluxVector_.z() = amf[2];
683 std::string fluxStr = rnemdParams->getFluxType();
685 if (fluxStr.find(
"Lx") != std::string::npos) {
687 }
else if (fluxStr.find(
"Ly") != std::string::npos) {
694 fluxVector_.normalize();
696 setOutputName(
getPrefix(filename) +
".rnemdRTheta");
699 r_.units =
"Angstroms";
701 for (
int i = 0; i < nBins_; i++)
702 r_.accumulator.push_back(
705 angularVelocity_.units =
"1/fs";
706 angularVelocity_.title =
"Projected Angular Velocity";
707 for (
unsigned int i = 0; i < nBins_; i++) {
708 angularVelocity_.accumulator.push_back(
713 std::pair<int, int> RNEMDRTheta::getBins(Vector3d pos) {
714 std::pair<int, int> result;
716 Vector3d rPos = pos - coordinateOrigin_;
717 RealType cosAngle =
dot(rPos, fluxVector_) / rPos.
length();
719 result.first = int(rPos.
length() / binWidth_);
720 result.second = int((nAngleBins_)*0.5 * (cosAngle + 1.0));
724 void RNEMDRTheta::processFrame(
int istep) {
725 ShellStatistics::processFrame(istep);
727 if (evaluator_.isDynamic()) {
728 seleMan_.setSelectionSet(evaluator_.evaluate());
734 std::vector<std::vector<int>> binCount(nBins_);
735 std::vector<std::vector<Mat3x3d>> binI(nBins_);
736 std::vector<std::vector<Vector3d>> binL(nBins_);
738 for (std::size_t i {}; i < nBins_; ++i) {
739 binCount[i].resize(nAngleBins_);
740 binI[i].resize(nAngleBins_);
741 binL[i].resize(nAngleBins_);
745 for (sd = seleMan_.beginSelected(i); sd != NULL;
746 sd = seleMan_.nextSelected(i)) {
748 std::pair<int, int> bins = getBins(sd->
getPos());
750 if (bins.first >= 0 && bins.first <
int(nBins_)) {
751 if (bins.second >= 0 && bins.second < nAngleBins_) {
752 Vector3d rPos = sd->
getPos() - coordinateOrigin_;
753 Vector3d vel = sd->
getVel();
755 Vector3d L = m *
cross(rPos, vel);
757 I = outProduct(rPos, rPos) * m;
763 binI[bins.first][bins.second] += I;
764 binL[bins.first][bins.second] += L;
765 binCount[bins.first][bins.second]++;
770 for (
unsigned int i = 0; i < nBins_; i++) {
771 RealType r = (((RealType)i + 0.5) * binWidth_);
772 r_.accumulator[i]->add(r);
774 std::vector<RealType> projections(nAngleBins_);
776 for (
int j = 0; j < nAngleBins_; j++) {
779 if (binCount[i][j] > 0) { omega = binI[i][j].inverse() * binL[i][j]; }
782 projections[j] =
dot(omega, fluxVector_);
785 angularVelocity_.accumulator[i]->add(projections);
789 void RNEMDRTheta::writeOutput() {
790 std::ofstream outStream(outputFilename_.c_str());
792 if (outStream.is_open()) {
794 outStream <<
"# SPATIAL STATISTICS\n";
795 outStream <<
"#nBins = " << nBins_ <<
"\t binWidth = " << binWidth_
796 <<
" maxR = " << nBins_ * binWidth_ <<
"\n";
797 outStream <<
"#fluxVector = " << fluxVector_ <<
"\tBins = " << nAngleBins_
799 outStream <<
"#\t" << angularVelocity_.title <<
"("
800 << angularVelocity_.units <<
")\t\t";
802 outStream << std::endl;
804 outStream.precision(8);
806 for (
unsigned int i = 0; i < nBins_; i++) {
807 std::size_t n {r_.accumulator[i]->getCount()};
810 std::string message =
811 "StaticAnalyser detected a numerical error writing: " +
812 angularVelocity_.title +
" for bin " + std::to_string(i);
814 angularVelocity_.accumulator[i]->writeData(outStream, message);
817 outStream << std::endl;
AtomType * getAtomType()
Returns the AtomType of this Atom.
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.
AtomTypeSet getSelectedAtomTypes()
getSelectedAtomTypes
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
"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.
RealType getFlucQPos()
Returns the current fluctuating charge 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.
RealType getFlucQVel()
Returns the current charge velocity of this stuntDouble.
Real length() const
Returns the length of this vector.
Real lengthSquare() const
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).
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
Vector3< Real > cross(const Vector3< Real > &v1, const Vector3< Real > &v2)
Returns the cross product of two Vectors.
Real dot(const DynamicVector< Real > &v1, const DynamicVector< Real > &v2)
Returns the dot product of two DynamicVectors.
std::string getPrefix(const std::string &str)