1 /*
2  * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 // Local private VOTCA includes
19 #include "eanalyze.h"
20 #include "votca/xtp/qmstate.h"
21 
22 namespace votca {
23 namespace xtp {
24 
ParseOptions(const tools::Property & options)25 void EAnalyze::ParseOptions(const tools::Property &options) {
26 
27   resolution_pairs_ = options.get(".resolution_pairs").as<double>();
28   resolution_sites_ = options.get(".resolution_sites").as<double>();
29   resolution_spatial_ = options.get(".resolution_spatial").as<double>();
30   seg_pattern_ = options.get(".match_pattern").as<std::string>();
31 
32   states_ = options.get(".states").as<std::vector<QMStateType>>();
33 
34   doenergy_landscape_ = options.get(".output_energy_landscape").as<bool>();
35 
36   if (options.exists(".distancemode")) {
37     std::string distancemode = options.get("distancemode").as<std::string>();
38     if (distancemode == "centerofmass") {
39       atomdistances_ = false;
40     } else {
41       atomdistances_ = true;
42     }
43   }
44 }
45 
Evaluate(Topology & top)46 bool EAnalyze::Evaluate(Topology &top) {
47 
48   // Short-list segments according to pattern
49   for (Segment &seg : top.Segments()) {
50     std::string seg_name = seg.getType();
51     if (votca::tools::wildcmp(seg_pattern_, seg_name)) {
52       seg_shortlist_.push_back(&seg);
53     }
54   }
55   std::cout << std::endl
56             << "... ... Short-listed " << seg_shortlist_.size()
57             << " segments (pattern='" << seg_pattern_ << "')" << std::flush;
58   std::cout
59       << std::endl
60       << "... ... ... NOTE Statistics of site energies and spatial"
61       << " correlations thereof are based on the short-listed segments only. "
62       << std::flush;
63   std::cout << std::endl
64             << "... ... ...      "
65             << "Statistics of site-energy differences operate on the full list."
66             << std::flush;
67 
68   // Calculate
69   // ... Site-energy histogram, mean, width
70   // ... Pair-energy histogram, mean, width
71   // ... Site-energy correlation
72 
73   const QMNBList &nblist = top.NBList();
74 
75   for (QMStateType state : states_) {
76     std::cout << std::endl
77               << "... ... excited state " << state.ToString() << std::flush;
78 
79     if (!seg_shortlist_.size()) {
80       std::cout << std::endl
81                 << "... ... ... No segments short-listed. Skip ... "
82                 << std::flush;
83     } else {
84       SiteHist(state);
85       SiteCorr(top, state);
86     }
87 
88     if (!nblist.size()) {
89       std::cout << std::endl
90                 << "... ... ... No pairs in topology. Skip ... " << std::flush;
91     } else {
92       PairHist(top, state);
93     }
94   }
95   std::cout << std::endl;
96   return true;
97 }
98 
SiteHist(QMStateType state) const99 void EAnalyze::SiteHist(QMStateType state) const {
100 
101   std::vector<double> Es;
102   Es.reserve(seg_shortlist_.size());
103   for (Segment *seg : seg_shortlist_) {
104     Es.push_back(seg->getSiteEnergy(state) * tools::conv::hrt2ev);
105   }
106 
107   double MAX = *std::max_element(Es.begin(), Es.end());
108   double MIN = *std::min_element(Es.begin(), Es.end());
109   double sum = std::accumulate(Es.begin(), Es.end(), 0.0);
110   double AVG = sum / double(Es.size());
111   double sq_sum = std::inner_product(Es.begin(), Es.end(), Es.begin(), 0.0);
112   double STD = std::sqrt(sq_sum / double(Es.size()) - AVG * AVG);
113 
114   // Prepare bins
115   Index BIN = Index((MAX - MIN) / resolution_sites_ + 0.5) + 1;
116 
117   tools::HistogramNew hist;
118   hist.Initialize(MIN, MAX, BIN);
119   hist.ProcessRange<std::vector<double>::iterator>(Es.begin(), Es.end());
120   tools::Table &tab = hist.data();
121   tab.flags() = std::vector<char>(tab.size(), ' ');
122   std::string comment =
123       (boost::format("EANALYZE: SITE-ENERGY HISTOGRAM[eV] \n # AVG %1$4.7f STD "
124                      "%2$4.7f MIN %3$4.7f MAX %4$4.7f") %
125        AVG % STD % MIN % MAX)
126           .str();
127   std::string filename = "eanalyze.sitehist_" + state.ToString() + ".out";
128   tab.set_comment(comment);
129   tab.Save(filename);
130 
131   // Write "seg x y z energy" with atomic {x,y,z}
132   if (doenergy_landscape_) {
133     std::string filename2 = "eanalyze.landscape_" + state.ToString() + ".out";
134     std::ofstream out;
135     out.open(filename2);
136     if (!out) {
137       throw std::runtime_error("error, cannot open file " + filename2);
138     }
139     for (Segment *seg : seg_shortlist_) {
140       if (seg->getId() < first_seg_) {
141         continue;
142       }
143       if (seg->getId() == last_seg_) {
144         break;
145       }
146       double E = seg->getSiteEnergy(state);
147       for (Atom &atm : *seg) {
148         out << boost::format("%1$3s %2$4.7f %3$4.7f %4$4.7f %5$4.7f\n") %
149                    seg->getType() % atm.getPos().x() % atm.getPos().y() %
150                    atm.getPos().z() % E;
151       }
152     }
153     out.close();
154   }
155 }
156 
PairHist(const Topology & top,QMStateType state) const157 void EAnalyze::PairHist(const Topology &top, QMStateType state) const {
158 
159   const QMNBList &nblist = top.NBList();
160 
161   std::string filenamelist = "eanalyze.pairlist_" + state.ToString() + ".out";
162 
163   // Collect site-energy differences from neighbourlist
164   std::vector<double> dE;
165   dE.reserve(2 * nblist.size());
166   std::ofstream out;
167   out.open(filenamelist);
168   if (!out) {
169     throw std::runtime_error("error, cannot open file " + filenamelist);
170   }
171   for (QMPair *pair : nblist) {
172     double deltaE = pair->getdE12(state) * tools::conv::hrt2ev;
173     dE.push_back(deltaE);
174     dE.push_back(-deltaE);
175     out << boost::format("%1$5d %2$5d %3$4.7f \n") % pair->Seg1()->getId() %
176                pair->Seg2()->getId() % deltaE;
177   }
178   out.close();
179 
180   double MAX = *std::max_element(dE.begin(), dE.end());
181   double MIN = *std::min_element(dE.begin(), dE.end());
182   double sum = std::accumulate(dE.begin(), dE.end(), 0.0);
183   double AVG = sum / double(dE.size());
184   double sq_sum = std::inner_product(dE.begin(), dE.end(), dE.begin(), 0.0);
185   double STD = std::sqrt(sq_sum / double(dE.size()) - AVG * AVG);
186   Index BIN = Index((MAX - MIN) / resolution_pairs_ + 0.5) + 1;
187 
188   std::string filename2 = "eanalyze.pairhist_" + state.ToString() + ".out";
189   tools::HistogramNew hist;
190   hist.Initialize(MIN, MAX, BIN);
191   hist.ProcessRange<std::vector<double>::iterator>(dE.begin(), dE.end());
192   tools::Table &tab = hist.data();
193   std::string comment =
194       (boost::format("EANALYZE: PAIR-ENERGY HISTOGRAM[eV] \n # AVG %1$4.7f STD "
195                      "%2$4.7f MIN %3$4.7f MAX %4$4.7f") %
196        AVG % STD % MIN % MAX)
197           .str();
198   tab.set_comment(comment);
199   tab.flags() = std::vector<char>(tab.size(), ' ');
200   tab.Save(filename2);
201 }
202 
SiteCorr(const Topology & top,QMStateType state) const203 void EAnalyze::SiteCorr(const Topology &top, QMStateType state) const {
204 
205   std::vector<double> Es;
206   Es.reserve(seg_shortlist_.size());
207   for (Segment *seg : seg_shortlist_) {
208     Es.push_back(seg->getSiteEnergy(state) * tools::conv::hrt2ev);
209   }
210 
211   double sum = std::accumulate(Es.begin(), Es.end(), 0.0);
212   double AVG = sum / double(Es.size());
213   double sq_sum = std::inner_product(Es.begin(), Es.end(), Es.begin(), 0.0);
214   double VAR = sq_sum / double(Es.size()) - AVG * AVG;
215   double STD = std::sqrt(VAR);
216 
217   // Collect inter-site distances, correlation product
218   tools::Table tabcorr;
219   Index length =
220       Index(seg_shortlist_.size()) * Index(seg_shortlist_.size() - 1) / 2;
221   tabcorr.resize(length);
222   Index index = 0;
223   for (Index i = 0; i < Index(seg_shortlist_.size()); i++) {
224     const Segment &segi = *seg_shortlist_[i];
225     for (Index j = i + 1; j < Index(seg_shortlist_.size()); j++) {
226       const Segment &segj = *seg_shortlist_[j];
227       double R = 0;
228       if (atomdistances_) {
229         R = top.GetShortestDist(segi, segj);
230       } else {
231         R = (top.PbShortestConnect(segi.getPos(), segj.getPos())).norm();
232       }
233 
234       double C =
235           (segi.getSiteEnergy(state) - AVG) * (segj.getSiteEnergy(state) - AVG);
236       tabcorr.set(index, R * tools::conv::bohr2nm, C);
237       index++;
238     }
239   }
240 
241   double MIN = tabcorr.x().minCoeff();
242   double MAX = tabcorr.x().maxCoeff();
243 
244   // Prepare bins
245   Index BIN = Index((MAX - MIN) / resolution_spatial_ + 0.5) + 1;
246   std::vector<std::vector<double>> histCs;
247   histCs.resize(BIN);
248 
249   for (Index i = 0; i < tabcorr.size(); ++i) {
250     Index bin = Index((tabcorr.x()[i] - MIN) / resolution_spatial_ + 0.5);
251     histCs[bin].push_back(tabcorr.y()[i]);
252   }
253 
254   tools::Table histC;
255   histC.SetHasYErr(true);
256   // Calculate spatial correlation
257   histC.resize(BIN);
258   for (Index bin = 0; bin < BIN; ++bin) {
259     double corr = 0.0;
260     double dcorr2 = 0.0;
261     for (double entry : histCs[bin]) {
262       corr += entry / VAR;
263     }
264     corr = corr / double(histCs[bin].size());
265     for (Index i = 0; i < Index(histCs[bin].size()); ++i) {
266       dcorr2 += (histCs[bin][i] / VAR / double(histCs[bin].size()) - corr) *
267                 (histCs[bin][i] / VAR / double(histCs[bin].size()) - corr);
268     }
269     // error on mean value
270     dcorr2 =
271         dcorr2 / double(histCs[bin].size()) / double(histCs[bin].size() - 1);
272     double R = MIN + double(bin) * resolution_spatial_;
273     histC.set(bin, R, corr, ' ', std::sqrt(dcorr2));
274   }
275 
276   std::string filename = "eanalyze.sitecorr_" + state.ToString() + ".out";
277   std::string comment =
278       (boost::format("EANALYZE: DISTANCE[nm] SPATIAL SITE-ENERGY "
279                      "CORRELATION[eV] \n # AVG "
280                      "%1$4.7f STD %2$4.7f MIN %3$4.7f MAX %4$4.7f") %
281        AVG % STD % MIN % MAX)
282           .str();
283   histC.set_comment(comment);
284   histC.Save(filename);
285 }
286 
287 }  // namespace xtp
288 }  // namespace votca
289