Scine::Readuct  6.0.0
This is the SCINE module ReaDuct.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
HessianTask.h
Go to the documentation of this file.
1 
7 #ifndef READUCT_HESSIANTASK_H_
8 #define READUCT_HESSIANTASK_H_
9 
10 /* Readuct */
11 #include "Tasks/Task.h"
12 /* Scine */
20 #include <Utils/Properties/Thermochemistry/ThermochemistryCalculator.h>
21 /* External */
22 #include "boost/exception/diagnostic_information.hpp"
23 #include <boost/filesystem.hpp>
24 /* std c++ */
25 #include <cstdio>
26 #include <fstream>
27 #include <iostream>
28 
29 namespace Scine {
30 namespace Readuct {
31 
32 class HessianTask : public Task {
33  public:
40  HessianTask(std::vector<std::string> input, std::vector<std::string> output, std::shared_ptr<Core::Log> logger = nullptr)
41  : Task(std::move(input), std::move(output), std::move(logger)) {
42  }
43 
44  std::string name() const override {
45  return "Hessian Calculation";
46  }
47 
48  bool run(SystemsMap& systems, Utils::UniversalSettings::ValueCollection taskSettings, bool testRunOnly = false,
49  std::vector<std::function<void(const int&, const Utils::AtomCollection&, const Utils::Results&, const std::string&)>>
50  observers = {}) const final {
53 
54  // Read and delete special settings
55  bool stopOnError = stopOnErrorExtraction(taskSettings);
56  bool silentCalculator = taskSettings.extract("silent_stdout_calculator", true);
57  if (!taskSettings.empty()) {
58  throw std::logic_error(falseTaskSettingsErrorMessage(name()));
59  }
60  if (observers.size() > 0) {
61  throw std::logic_error("HessianTask does not feature algorithm accepting observers, yet one was given");
62  }
63 
64  // If no errors encountered until here, the basic settings should be alright
65  if (testRunOnly) {
66  return true;
67  }
68 
69  // Note: _input is guaranteed not to be empty by Task constructor
70  auto calc = copyCalculator(systems, _input.front(), name());
71  auto previousResults = calc->results();
72  Utils::CalculationRoutines::setLog(*calc, true, true, !silentCalculator);
73 
74  // Check system size
75  const bool singleAtom = calc->getStructure()->size() == 1;
76  if (calc->getStructure()->size() == 1) {
77  const auto hessian = Eigen::MatrixXd::Zero(3, 3);
78  calc->results().set<Utils::Property::Hessian>(hessian);
79  calc->results().set<Utils::Property::PartialHessian>(Utils::PartialHessian(hessian, {0}));
80  // Some calculators may remove the Hessian if only the energy is calculated.
81  previousResults.set<Utils::Property::Hessian>(hessian);
82  previousResults.set<Utils::Property::PartialHessian>(Utils::PartialHessian(hessian, {0}));
83  }
84  // Get/calculate Hessian
85  if (!(calc->results().has<Utils::Property::Hessian>() || calc->results().has<Utils::Property::PartialHessian>()) ||
86  !calc->results().has<Utils::Property::Energy>()) {
87  // has neither type of Hessian or has no energy
88  Utils::PropertyList requiredProperties = Utils::Property::Energy;
89  if (!calc->results().has<Utils::Property::Thermochemistry>() &&
90  calc->possibleProperties().containsSubSet(Utils::Property::Thermochemistry) && !singleAtom) {
91  requiredProperties.addProperty(Utils::Property::Thermochemistry);
92  // many calculators cannot handle that only Thermochemistry is requested
93  // because they delete their results and then assume that Hessian was also calculated
94  if (calc->possibleProperties().containsSubSet(Utils::Property::PartialHessian)) {
95  requiredProperties.addProperty(Utils::Property::PartialHessian);
96  }
97  else if (calc->possibleProperties().containsSubSet(Utils::Property::Hessian)) {
98  requiredProperties.addProperty(Utils::Property::Hessian);
99  }
100  }
101  else if (!calc->results().has<Utils::Property::PartialHessian>() &&
102  calc->possibleProperties().containsSubSet(Utils::Property::PartialHessian)) {
103  requiredProperties.addProperty(Utils::Property::PartialHessian);
104  }
105  else if (!calc->results().has<Utils::Property::Hessian>() &&
106  calc->possibleProperties().containsSubSet(Utils::Property::Hessian)) {
107  requiredProperties.addProperty(Utils::Property::Hessian);
108  }
109  calc->setRequiredProperties(requiredProperties);
110  try {
111  calc->calculate(name());
112  if (!calc->results().get<Utils::Property::SuccessfulCalculation>()) {
113  throw std::runtime_error(name() + " was not successful");
114  }
115  }
116  catch (...) {
117  if (stopOnError) {
118  throw;
119  }
120  _logger->warning
121  << " " + name() + " was not successful with error:\n " + boost::current_exception_diagnostic_information()
122  << Core::Log::endl;
123  calc->results() = previousResults + calc->results();
124  return false;
125  }
126  }
127  calc->results() = previousResults + calc->results();
128  Utils::NormalModesContainer modes;
129  auto system = calc->getStructure();
130  // Get normal modes
131  if (calc->results().has<Utils::Property::Hessian>()) {
132  auto hessian = calc->results().get<Utils::Property::Hessian>();
133  modes = Utils::NormalModeAnalysis::calculateNormalModes(hessian, system->getElements(), system->getPositions());
134  }
135  else if (calc->results().has<Utils::Property::PartialHessian>()) {
136  auto partialHessian = calc->results().get<Utils::Property::PartialHessian>();
137  modes = Utils::NormalModeAnalysis::calculateNormalModes(partialHessian, system->getElements(), system->getPositions());
138  }
139  else {
140  throw std::runtime_error("Hessian or partial Hessian properties not available in results!");
141  }
142  auto wavenumbers = modes.getWaveNumbers();
143  // Print frequencies
144  auto cout = _logger->output;
145  cout << " Vib. Frequencies:\n";
146  cout << " [Rot. and trans. vib. removed, imaginary freq. shown as negative.]\n\n";
147  cout.printf(" %4s %8s\n", "#", "cm^-1");
148  int skipped = 0;
149  for (unsigned int i = 0; i < wavenumbers.size(); i++) {
150  if (std::fabs(wavenumbers[i]) < 1e-6) {
151  ++skipped;
152  continue;
153  }
154  cout.printf(" %4d %+8.1f\n", i + 1, wavenumbers[i]);
155  }
156  if (skipped) {
157  cout << " Skipped " << skipped << " modes with zero frequency." << Core::Log::endl;
158  }
159  cout << Core::Log::endl;
160 
161  const std::string& outputSystem = (!_output.empty() ? _output[0] : _input[0]);
162 
163  // Setup output directory if needed
164  boost::filesystem::path dir(outputSystem);
165  if (!singleAtom && wavenumbers[0] < 0.0) {
166  boost::filesystem::create_directory(dir);
167  }
168  // Print Imaginary modes
169  for (unsigned int i = 0; i < wavenumbers.size() && wavenumbers[i] < 0.0; ++i) {
170  auto trj = modes.getModeAsMolecularTrajectory(i, *system, 1.0);
171  std::string filename = Utils::format("%s.vibmode-%05d.trj.xyz", outputSystem.c_str(), i + 1);
172  std::ofstream xyz((dir / filename).string(), std::ofstream::out);
174  xyz.close();
175  }
176 
177  // Store result
178  systems[outputSystem] = calc;
179 
180  if (singleAtom) {
181  const auto hessian = calc->results().get<Utils::Property::Hessian>();
182  auto thermoCalc = Scine::Utils::ThermochemistryCalculator(
183  hessian, *calc->getStructure(), calc->settings().getInt(Utils::SettingsNames::spinMultiplicity),
184  calc->results().get<Utils::Property::Energy>());
185  calc->results().set<Utils::Property::Thermochemistry>(thermoCalc.calculate());
186  }
187 
188  // Print thermochemistry results
189  if (calc->results().has<Utils::Property::Thermochemistry>()) {
190  auto thermo = calc->results().get<Utils::Property::Thermochemistry>();
191  double temp = calc->settings().getDouble("temperature");
192  double pressure = calc->settings().getDouble("pressure");
193  cout << " Thermochemical Data:\n";
194  cout.printf(" %35s %+14.9f K\n", "Temperature (T):", temp);
195  cout.printf(" %35s %+14.6f Pa\n", "Pressure (P):", pressure);
196  cout.printf(" %35s %14d\n", "Molecular symmetry number:", thermo.overall.symmetryNumber);
197  cout.printf(" %35s %+14.9f E_h\n", "Zero Point Vibrational Energy:", thermo.overall.zeroPointVibrationalEnergy);
198  cout.printf(" %35s %+14.9f E_h/K\n", "Entropy (S):", thermo.overall.entropy);
199  cout.printf(" %35s %+14.9f E_h\n", "Entropy (-TS):", -temp * thermo.overall.entropy);
200  cout.printf(" %35s %+14.9f E_h\n", "Enthalpy (H):", thermo.overall.enthalpy);
201  double electronicEnergy = calc->results().get<Utils::Property::Energy>();
202  cout.printf(" %35s %+14.9f E_h\n", "Gibbs Energy Correction (dG):", thermo.overall.gibbsFreeEnergy - electronicEnergy);
203  cout.printf(" %35s %+14.9f E_h\n", "Electronic Energy (E):", electronicEnergy);
204  cout.printf(" %35s %+14.9f E_h\n", "Gibbs Energy (E + dG):", thermo.overall.gibbsFreeEnergy);
205  cout << Core::Log::endl << Core::Log::endl;
206  }
207  return true;
208  }
209 };
210 
211 } // namespace Readuct
212 } // namespace Scine
213 
214 #endif // READUCT_HESSIANTASK_H_
Definition: HessianTask.h:32
HessianTask(std::vector< std::string > input, std::vector< std::string > output, std::shared_ptr< Core::Log > logger=nullptr)
Construct a new HessianTask.
Definition: HessianTask.h:40
void warningIfMultipleOutputsGiven() const
Warn if more than one output system was specified.
Definition: Task.h:107
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 void write(format f, const std::string &fileName, const MolecularTrajectory &m, const BondOrderCollection &bondOrders)
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: HessianTask.h:48
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
std::string name() const override
Getter for the tasks name.
Definition: HessianTask.h:44