7 #ifndef READUCT_SINGLEPOINTTASK_H_
8 #define READUCT_SINGLEPOINTTASK_H_
20 #include "boost/exception/diagnostic_information.hpp"
21 #include <boost/filesystem.hpp>
44 :
Task(std::move(input), std::move(output), std::move(logger)) {
47 std::string
name()
const override {
48 return "Single Point Calculation";
53 observers = {})
const final {
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";
74 throw std::logic_error(
"Specified one or more task settings that are not available for this task:" + keyListing);
76 if (observers.size() > 0) {
77 throw std::logic_error(
"SinglePointTask does not feature algorithm accepting observers, yet one was given");
85 auto calc = copyCalculator(systems, _input.front(),
name());
86 const auto previousResults = calc->results();
87 Utils::CalculationRoutines::setLog(*calc,
true,
true, !silentCalculator);
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");
100 if (requireGradients && !gradientsAvailable) {
101 throw std::logic_error(
"Gradients required, but chosen calculator does not provide them.");
103 if (requireStressTensor && !stressTensorAvailable) {
104 throw std::logic_error(
"Stress tensor required, but chosen calculator does not provide it.");
106 if (requireOrbitalEnergies && !orbitalEnergiesAvailable) {
107 throw std::logic_error(
"Orbital energies required, but chosen calculator does not provide them.");
109 if (requireBondOrders && !bondOrdersAvailable) {
110 throw std::logic_error(
"Bond orders required, but chosen calculator does not provide them.");
112 if (requirePartialEnergies && !partialEnergiesAvailable) {
113 throw std::logic_error(
"Partial energies were required, but the chosen calculator does not provide them.");
115 if (requirePartialGradients && !partialGradientsAvailable) {
116 throw std::logic_error(
"Partial gradients were required, but the chosen calculator does not provide them.");
119 Utils::PropertyList requiredProperties = Utils::Property::Energy;
120 if (requireCharges) {
121 requiredProperties.addProperty(Utils::Property::AtomicCharges);
123 if (requireGradients) {
124 requiredProperties.addProperty(Utils::Property::Gradients);
126 if (requireStressTensor) {
127 requiredProperties.addProperty(Utils::Property::StressTensor);
129 if (requireOrbitalEnergies) {
130 requiredProperties.addProperty(Utils::Property::OrbitalEnergies);
132 if (requireBondOrders) {
133 requiredProperties.addProperty(Utils::Property::BondOrderMatrix);
135 if (requirePartialEnergies) {
136 requiredProperties.addProperty(Utils::Property::PartialEnergies);
138 if (requirePartialGradients) {
139 requiredProperties.addProperty(Utils::Property::PartialGradients);
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");
154 <<
" " +
name() +
" was not successful with error:\n " + boost::current_exception_diagnostic_information()
156 calc->results() = previousResults + calc->results();
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;
166 calc = Utils::CalculationRoutines::spinPropensity(*calc, *_logger, spinPropensityCheck);
169 if (!calc->getRequiredProperties().containsSubSet(requiredProperties)) {
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");
182 <<
" " +
name() +
" was not successful with error:\n " + boost::current_exception_diagnostic_information()
184 calc->results() = previousResults + calc->results();
190 calc->results() = previousResults + calc->results();
192 if (!_output.empty()) {
193 systems[_output[0]] = calc;
196 systems[_input[0]] = calc;
199 auto energy = calc->results().get<Utils::Property::Energy>();
202 auto cout = _logger->output;
203 cout.printf(
" The (electronic) energy is: %+16.9f hartree\n\n", energy);
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);
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++) {
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); };
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); };
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());
249 if (requireOrbitalEnergies) {
250 Utils::SingleParticleEnergies orbitalEnergies = calc->results().get<Utils::Property::OrbitalEnergies>();
252 cout <<
" Orbital Energies:\n\n";
254 if (orbitalEnergies.isRestricted()) {
255 const auto restrictedEnergies = orbitalEnergies.getRestrictedEnergies();
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;
264 const auto alphaEnergies = orbitalEnergies.getAlphaEnergies();
265 const auto betaEnergies = orbitalEnergies.getBetaEnergies();
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;
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;
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"]);
288 if (partiaEnergies.find(
"mm_energy") != partiaEnergies.end()) {
289 cout.printf(
" The MM energy is: %+16.9f hartree\n", partiaEnergies[
"mm_energy"]);
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); };
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); };
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)