Scine::Readuct  6.0.0
This is the SCINE module ReaDuct.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
SinglePointTask.h
Go to the documentation of this file.
1 
7 #ifndef READUCT_SINGLEPOINTTASK_H_
8 #define READUCT_SINGLEPOINTTASK_H_
9 
10 /* Readuct */
11 #include "Tasks/Task.h"
12 /* Scine */
14 #include <Utils/CalculatorBasics.h>
16 #include <Utils/IO/Yaml.h>
17 #include <Utils/Settings.h>
19 /* External */
20 #include "boost/exception/diagnostic_information.hpp"
21 #include <boost/filesystem.hpp>
22 /* std c++ */
23 #include <algorithm>
24 #include <cstdio>
25 #include <fstream>
26 #include <iomanip>
27 #include <iostream>
28 #include <numeric>
29 #include <random>
30 #include <vector>
31 
32 namespace Scine {
33 namespace Readuct {
34 
35 class SinglePointTask : public Task {
36  public:
43  SinglePointTask(std::vector<std::string> input, std::vector<std::string> output, std::shared_ptr<Core::Log> logger = nullptr)
44  : Task(std::move(input), std::move(output), std::move(logger)) {
45  }
46 
47  std::string name() const override {
48  return "Single Point Calculation";
49  }
50 
51  bool run(SystemsMap& systems, Utils::UniversalSettings::ValueCollection taskSettings, bool testRunOnly = false,
52  std::vector<std::function<void(const int&, const Utils::AtomCollection&, const Utils::Results&, const std::string&)>>
53  observers = {}) const final {
56 
57  // Read and delete special settings
58  bool stopOnError = stopOnErrorExtraction(taskSettings);
59  bool requireCharges = taskSettings.extract("require_charges", true);
60  bool requireGradients = taskSettings.extract("require_gradients", false);
61  bool requireStressTensor = taskSettings.extract("require_stress_tensor", false);
62  bool requireBondOrders = taskSettings.extract("require_bond_orders", false);
63  bool requirePartialEnergies = taskSettings.extract("require_partial_energies", false);
64  bool requirePartialGradients = taskSettings.extract("require_partial_gradients", false);
65  bool requireOrbitalEnergies = taskSettings.extract("orbital_energies", false);
66  bool silentCalculator = taskSettings.extract("silent_stdout_calculator", true);
67  int spinPropensityCheck = taskSettings.extract("spin_propensity_check", 0);
68  if (!taskSettings.empty()) {
69  std::string keyListing = "\n";
70  auto keys = taskSettings.getKeys();
71  for (const auto& key : keys) {
72  keyListing += "'" + key + "'\n";
73  }
74  throw std::logic_error("Specified one or more task settings that are not available for this task:" + keyListing);
75  }
76  if (observers.size() > 0) {
77  throw std::logic_error("SinglePointTask does not feature algorithm accepting observers, yet one was given");
78  }
79 
80  if (testRunOnly) {
81  return true; // leave out rest in case of task chaining
82  }
83 
84  // Note: _input is guaranteed not to be empty by Task constructor
85  auto calc = copyCalculator(systems, _input.front(), name());
86  const auto previousResults = calc->results();
87  Utils::CalculationRoutines::setLog(*calc, true, true, !silentCalculator);
88  // Check for available properties
89  const bool chargesAvailable = calc->possibleProperties().containsSubSet(Utils::Property::AtomicCharges);
90  const bool gradientsAvailable = calc->possibleProperties().containsSubSet(Utils::Property::Gradients);
91  const bool stressTensorAvailable = calc->possibleProperties().containsSubSet(Utils::Property::StressTensor);
92  const bool orbitalEnergiesAvailable = calc->possibleProperties().containsSubSet(Utils::Property::OrbitalEnergies);
93  const bool bondOrdersAvailable = calc->possibleProperties().containsSubSet(Utils::Property::BondOrderMatrix);
94  const bool partialEnergiesAvailable = calc->possibleProperties().containsSubSet(Utils::Property::PartialEnergies);
95  const bool partialGradientsAvailable = calc->possibleProperties().containsSubSet(Utils::Property::PartialGradients);
96  if (requireCharges && !chargesAvailable) {
97  throw std::logic_error("Charges required, but chosen calculator does not provide them.\n"
98  "If you do not need charges, set 'require_charges' to 'false' in the task settings");
99  }
100  if (requireGradients && !gradientsAvailable) {
101  throw std::logic_error("Gradients required, but chosen calculator does not provide them.");
102  }
103  if (requireStressTensor && !stressTensorAvailable) {
104  throw std::logic_error("Stress tensor required, but chosen calculator does not provide it.");
105  }
106  if (requireOrbitalEnergies && !orbitalEnergiesAvailable) {
107  throw std::logic_error("Orbital energies required, but chosen calculator does not provide them.");
108  }
109  if (requireBondOrders && !bondOrdersAvailable) {
110  throw std::logic_error("Bond orders required, but chosen calculator does not provide them.");
111  }
112  if (requirePartialEnergies && !partialEnergiesAvailable) {
113  throw std::logic_error("Partial energies were required, but the chosen calculator does not provide them.");
114  }
115  if (requirePartialGradients && !partialGradientsAvailable) {
116  throw std::logic_error("Partial gradients were required, but the chosen calculator does not provide them.");
117  }
118  // Calculate energy, and possibly atomic charges and/or gradients
119  Utils::PropertyList requiredProperties = Utils::Property::Energy;
120  if (requireCharges) {
121  requiredProperties.addProperty(Utils::Property::AtomicCharges);
122  }
123  if (requireGradients) {
124  requiredProperties.addProperty(Utils::Property::Gradients);
125  }
126  if (requireStressTensor) {
127  requiredProperties.addProperty(Utils::Property::StressTensor);
128  }
129  if (requireOrbitalEnergies) {
130  requiredProperties.addProperty(Utils::Property::OrbitalEnergies);
131  }
132  if (requireBondOrders) {
133  requiredProperties.addProperty(Utils::Property::BondOrderMatrix);
134  }
135  if (requirePartialEnergies) {
136  requiredProperties.addProperty(Utils::Property::PartialEnergies);
137  }
138  if (requirePartialGradients) {
139  requiredProperties.addProperty(Utils::Property::PartialGradients);
140  }
141 
142  try {
143  calc->setRequiredProperties(requiredProperties);
144  calc->calculate(name());
145  if (!calc->results().get<Utils::Property::SuccessfulCalculation>()) {
146  throw std::runtime_error(name() + " was not successful");
147  }
148  }
149  catch (...) {
150  if (stopOnError) {
151  throw;
152  }
153  _logger->error
154  << " " + name() + " was not successful with error:\n " + boost::current_exception_diagnostic_information()
155  << Core::Log::endl;
156  calc->results() = previousResults + calc->results();
157  return false;
158  }
159 
160  if (spinPropensityCheck != 0) {
161  if (!calc->settings().valueExists(Utils::SettingsNames::spinMultiplicity)) {
162  _logger->warning << "Warning: " << calc->name()
163  << " does not allow multiplicity changes, skipping spin propensity check" << Core::Log::endl;
164  }
165  else {
166  calc = Utils::CalculationRoutines::spinPropensity(*calc, *_logger, spinPropensityCheck);
167  }
168  // some calculators are lazy and don't copy properties, hence we recalculate if necessary
169  if (!calc->getRequiredProperties().containsSubSet(requiredProperties)) {
170  try {
171  calc->setRequiredProperties(requiredProperties);
172  calc->calculate(name());
173  if (!calc->results().get<Utils::Property::SuccessfulCalculation>()) {
174  throw std::runtime_error(name() + " was not successful");
175  }
176  }
177  catch (...) {
178  if (stopOnError) {
179  throw;
180  }
181  _logger->error
182  << " " + name() + " was not successful with error:\n " + boost::current_exception_diagnostic_information()
183  << Core::Log::endl;
184  calc->results() = previousResults + calc->results();
185  return false;
186  }
187  }
188  }
189 
190  calc->results() = previousResults + calc->results();
191  // Store result
192  if (!_output.empty()) {
193  systems[_output[0]] = calc;
194  }
195  else {
196  systems[_input[0]] = calc;
197  }
198 
199  auto energy = calc->results().get<Utils::Property::Energy>();
200 
201  // Print energy
202  auto cout = _logger->output;
203  cout.printf(" The (electronic) energy is: %+16.9f hartree\n\n", energy);
204  // Print electronic temperature
205  if (calc->settings().valueExists(Utils::SettingsNames::electronicTemperature)) {
206  auto etemp = calc->settings().getDouble(Utils::SettingsNames::electronicTemperature);
207  cout.printf(" The (electronic) temperature was: %+10.3f K\n\n", etemp);
208  }
209  // Print charges
210  if (requireCharges) {
211  auto charges = calc->results().get<Utils::Property::AtomicCharges>();
212  auto atomColl = calc->getStructure();
213  auto elements = atomColl->getElements();
214  cout << " Atomic Partial Charges:\n\n";
215  for (size_t i = 0; i < charges.size(); i++) {
216  cout.printf(" %5d %-6s: %+11.6f\n", i, Utils::ElementInfo::symbol(elements[i]).c_str(), charges[i]);
217  }
218  }
219  cout << Core::Log::nl;
220  // Print gradients
221  if (requireGradients) {
222  auto gradients = calc->results().get<Utils::Property::Gradients>();
223  cout << " Gradients (hartree / bohr):\n\n";
224  cout << [&gradients](std::ostream& os) { Utils::matrixPrettyPrint(os, gradients); };
225  cout << Core::Log::nl;
226  }
227  // Print stress tensor
228  if (requireStressTensor) {
229  Eigen::Matrix3d stressTensor = calc->results().get<Utils::Property::StressTensor>();
230  cout << " Stress tensor (hartree / bohr^3):\n\n";
231  cout << [&stressTensor](std::ostream& os) { Utils::matrixPrettyPrint(os, stressTensor); };
232  cout << Core::Log::nl;
233  }
234  // Print bond orders
235  if (requireBondOrders) {
236  cout << " Atom#1 Atom#2 Bond Order\n\n";
237  auto bos = calc->results().get<Utils::Property::BondOrderMatrix>();
238  const auto& mat = bos.getMatrix();
239  for (int i = 0; i < mat.outerSize(); i++) {
240  for (typename Eigen::SparseMatrix<double>::InnerIterator it(mat, i); it; ++it) {
241  if (it.value() > 0.3 && it.row() < it.col()) {
242  cout.printf(" %6d %6d %+16.9f\n", it.row(), it.col(), it.value());
243  }
244  }
245  }
246  cout << Core::Log::nl << Core::Log::endl;
247  }
248  // Print orbital energies
249  if (requireOrbitalEnergies) {
250  Utils::SingleParticleEnergies orbitalEnergies = calc->results().get<Utils::Property::OrbitalEnergies>();
251 
252  cout << " Orbital Energies:\n\n";
253 
254  if (orbitalEnergies.isRestricted()) {
255  const auto restrictedEnergies = orbitalEnergies.getRestrictedEnergies();
256 
257  std::cout << std::setw(20) << "Index" << std::setw(20) << "Energy / Hartree" << std::endl;
258  for (unsigned int i = 0; i < restrictedEnergies.size(); ++i) {
259  std::cout << std::setprecision(7) << std::scientific << std::right << std::setw(5) << i << std::setw(5)
260  << restrictedEnergies[i] << std::endl;
261  }
262  }
263  else {
264  const auto alphaEnergies = orbitalEnergies.getAlphaEnergies();
265  const auto betaEnergies = orbitalEnergies.getBetaEnergies();
266 
267  cout << " Alpha spin:\n\n";
268  std::cout << std::setw(5) << " Index" << std::setw(20) << "Energy / Hartree" << std::endl;
269  for (unsigned int i = 0; i < alphaEnergies.size(); ++i) {
270  std::cout << std::setprecision(7) << std::scientific << std::right << std::setw(5) << i << std::setw(22)
271  << alphaEnergies[i] << std::endl;
272  }
273 
274  cout << "\n\n Beta spin:\n\n";
275  std::cout << std::setw(5) << " Index" << std::setw(20) << "Energy / Hartree" << std::endl;
276  for (unsigned int i = 0; i < betaEnergies.size(); ++i) {
277  std::cout << std::setprecision(7) << std::scientific << std::right << std::setw(5) << i << std::setw(22)
278  << betaEnergies[i] << std::endl;
279  }
280  }
281  }
282  if (requirePartialEnergies) {
283  auto partiaEnergies = calc->results().get<Utils::Property::PartialEnergies>();
284  cout << " Partial Energies:\n\n";
285  if (partiaEnergies.find("qm_energy") != partiaEnergies.end()) {
286  cout.printf(" The QM energy is: %+16.9f hartree\n", partiaEnergies["qm_energy"]);
287  }
288  if (partiaEnergies.find("mm_energy") != partiaEnergies.end()) {
289  cout.printf(" The MM energy is: %+16.9f hartree\n", partiaEnergies["mm_energy"]);
290  }
291  }
292  if (requirePartialGradients) {
293  auto partialGradients = calc->results().get<Utils::Property::PartialGradients>();
294  if (partialGradients.find("qm_gradients") != partialGradients.end()) {
295  const auto& qmGradients = partialGradients["qm_gradients"];
296  cout << " QM gradients (hartree / bohr):\n\n";
297  cout << [&qmGradients](std::ostream& os) { Utils::matrixPrettyPrint(os, qmGradients); };
298  cout << Core::Log::nl;
299  }
300  if (partialGradients.find("mm_gradients") != partialGradients.end()) {
301  const auto& mmGradients = partialGradients["mm_gradients"];
302  cout << " MM gradients (hartree / bohr):\n\n";
303  cout << [&mmGradients](std::ostream& os) { Utils::matrixPrettyPrint(os, mmGradients); };
304  cout << Core::Log::nl;
305  }
306  }
307 
308  cout << Core::Log::nl << Core::Log::endl;
309  return true;
310  }
311 };
312 
313 } // namespace Readuct
314 } // namespace Scine
315 
316 #endif // READUCT_SINGLEPOINTTASK_H_
SinglePointTask(std::vector< std::string > input, std::vector< std::string > output, std::shared_ptr< Core::Log > logger=nullptr)
Construct a new SinglePointTask.
Definition: SinglePointTask.h:43
std::string name() const override
Getter for the tasks name.
Definition: SinglePointTask.h:47
void warningIfMultipleOutputsGiven() const
Warn if more than one output system was specified.
Definition: Task.h:107
bool run(SystemsMap &systems, Utils::UniversalSettings::ValueCollection taskSettings, bool testRunOnly=false, std::vector< std::function< void(const int &, const Utils::AtomCollection &, const Utils::Results &, const std::string &)>> observers={}) const final
Executes the actual task represented by this class.
Definition: SinglePointTask.h:51
Definition: SinglePointTask.h:35
static std::ostream & endl(std::ostream &os)
const std::vector< std::string > & input() const
Getter for the expected names of the input systems.
Definition: Task.h:85
static std::ostream & nl(std::ostream &os)
void warningIfMultipleInputsGiven() const
Warn if more than one input system was specified.
Definition: Task.h:98
const std::vector< std::string > & output() const
Getter for the names of the output systems generated by this task.
Definition: Task.h:92
The base class for all tasks in Readuct.
Definition: Task.h:29
static std::string symbol(ElementType e)