Loading...
Searching...
No Matches
ParametersJSON.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12#include "eon/ParametersJSON.h"
13#include "eon/HelperFunctions.h"
14#include "eon/PIQTST.h"
15#include "eon/Parameters.h"
16#include "eon/ParametersINI.h"
17#include "magic_enum/magic_enum.hpp"
18
19#include <cctype>
20#include <cstdint>
21#include <format>
22#include <nlohmann/json.hpp>
23#include <sstream>
24#include <stdexcept>
25#include <vector>
26
27using json = nlohmann::json;
28
29// Helper: enum <-> string via magic_enum
30template <typename E> static json enum_to_json(E val) {
31 return std::string(magic_enum::enum_name(val));
32}
33
34template <typename E> static E enum_from_json(const json &j, E fallback) {
35 if (j.is_string()) {
36 auto result = magic_enum::enum_cast<E>(j.get<std::string>(),
37 magic_enum::case_insensitive);
38 return result.value_or(fallback);
39 }
40 return fallback;
41}
42
43// Macro for optional JSON field extraction with default
44#define JSON_OPT(j, key, target) \
45 if ((j).contains(key)) \
46 (target) = (j).at(key).get<decltype(target)>()
47
48namespace eonc::config {
49
51 json j;
52
53 // [Main]
54 j["Main"] = {
63 {"finite_difference",
66 {"remove_net_force",
68 {"write_con_forces",
70 };
71
72 // [Potential]
73 j["Potential"] = {
74 {"potential",
76 {"mpi_poll_period",
78 {"lammps_logging",
80 {"lammps_threads",
82 {"emt_rasmussen",
84 {"log_potential",
87 {"potentials_path",
89 };
90
91 // [Structure Comparison]
92 j["Structure Comparison"] = {
93 {"distance_difference",
96 {"neighbor_cutoff",
98 {"check_rotation",
100 {"indistinguishable_atoms",
103 {"energy_difference",
105 {"remove_translation",
108 };
109
110 // [Optimizer]
111 j["Optimizer"] = {
112 {"opt_method",
114 {"convergence_metric",
116 {"max_iterations",
119 {"converged_force",
122 {"max_time_step",
124 };
125 j["Optimizer"]["LBFGS"] = {
127 {"inverse_curvature",
129 {"max_inverse_curvature",
131 {"auto_scale",
133 {"angle_reset",
135 {"distance_reset",
138 {"project_rigid",
145 {"extra_updates",
147 {"cautious_eps",
149 {"cautious_alpha",
153 {"precon_rcut",
155 };
156 j["Optimizer"]["Xtsci"] = {
163 };
164
165 // [Dynamics]
166 j["Dynamics"] = {
169 };
170
171 // [Thermostat]
172 j["Thermostat"] = {
174 {"andersen_alpha",
176 {"andersen_collision_period",
179 {"langevin_friction",
182 {"path_springs",
184 {"path_eco_omega_max",
186 {"path_gle_file",
188 {"path_pile_tau",
190 {"path_pile_scale",
193 };
194
195 // [Nudged Elastic Band]
196 j["Nudged Elastic Band"] = {
199 {"opt_method",
203 {"solid_state_weight",
205 {"solid_state_pressure",
207 };
208 j["Nudged Elastic Band"]["spring"] = {
210 {"elastic_band",
212 {"doubly_nudged",
214 };
215 j["Nudged Elastic Band"]["climbing_image"] = {
217 {"converged_only",
219 {"band_slack",
221 };
222 {
223 const auto &zoom = ParametersLoadAccess::neb_options(p).zoom;
224 j["Nudged Elastic Band"]["zoom"] = {
225 {"enabled", zoom.enabled},
226 {"alpha", zoom.alpha},
227 {"offset", zoom.offset},
228 {"mode", enum_to_json(zoom.mode)},
229 {"activation_threshold", zoom.activation_threshold},
230 {"interpolation", enum_to_json(zoom.interpolation)},
231 {"stability_count", zoom.stability_count},
232 {"max_iterations", zoom.max_iterations},
233 };
234 }
235
236 // [Dimer]
237 j["Dimer"] = {
240 {"converged_angle",
243 {"opt_method",
245 {"rotation_backend",
247 {"lor_residual_tol",
249 };
250
251 // [Saddle Search]
252 j["Saddle Search"] = {
254 {"min_mode_method",
257 {"max_iterations",
259 {"displace_magnitude",
261 {"displace_radius",
263 };
264
265 // [Prefactor]
266 j["Prefactor"] = {
267 {"default_value",
271 };
272
273 // [Lanczos]
274 j["Lanczos"] = {
276 {"max_iterations",
280 };
281
282 // [Davidson]
283 j["Davidson"] = {
285 {"max_iterations",
287 {"diagonal_preconditioner",
290 };
291
292 // [Hessian]
293 j["Hessian"] = {
295 {"zero_freq_value",
299 {"checkpoint_path",
302 };
303
304 // [Instanton]
305 {
307 j["Instanton"] = {
308 {"mode", o.mode},
309 {"reactant_filename", o.reactant_filename},
310 {"product_filename", o.product_filename},
311 {"initial_path", o.initial_path},
312 {"beads", o.beads},
313 {"beta_hbar_omega", o.beta_hbar_omega},
314 {"max_iterations", o.max_iterations},
315 {"force_tolerance", o.force_tolerance},
316 {"hessian_stride", o.hessian_stride},
317 {"saddle_filename", o.saddle_filename},
318 {"temperature", o.temperature},
319 {"temperatures", o.temperatures},
320 {"half_ring", o.half_ring},
321 {"initial_hessians", o.initial_hessians},
322 {"energy_shift", o.energy_shift},
323 {"bead_ladder", o.bead_ladder},
324 {"hessian_final", o.hessian_final},
325 {"springs", o.springs},
326 {"pi_planes", o.pi_planes},
327 {"pi_beads", o.pi_beads},
328 {"pi_equilibration_steps", o.pi_equilibration_steps},
329 {"pi_sampling_steps", o.pi_sampling_steps},
330 {"pi_time_step", o.pi_time_step},
331 {"pi_thermostat", o.pi_thermostat},
332 {"pi_gle_file", o.pi_gle_file},
333 {"pi_pile_tau", o.pi_pile_tau},
334 {"pi_pile_scale", o.pi_pile_scale},
335 {"pi_seed", o.pi_seed},
336 {"pi_direction", o.pi_direction},
337 {"pi_reactant_extent", o.pi_reactant_extent},
338 {"pi_recrossing_parents", o.pi_recrossing_parents},
339 {"pi_recrossing_children", o.pi_recrossing_children},
340 {"pi_recrossing_time", o.pi_recrossing_time},
341 {"pi_recrossing_spacing", o.pi_recrossing_spacing},
342 };
343 }
344
345 // [Debug]
346 j["Debug"] = {
348 {"write_movies_interval",
350 {"write_deprecated_outs",
352 };
353
354 // [Serve]
355 j["Serve"] = {
361 };
362
363 return j;
364}
365
366static std::string lowerCopy(std::string value) {
367 for (char &ch : value) {
368 ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
369 }
370 return value;
371}
372
373static std::vector<double> temperaturesFromJson(const json &value) {
374 if (value.is_array()) {
375 return value.get<std::vector<double>>();
376 }
377 if (!value.is_string()) {
378 throw std::invalid_argument(
379 "[Instanton] temperatures must be a list or a comma-separated "
380 "string");
381 }
382 std::vector<double> out;
383 std::stringstream ss(value.get<std::string>());
384 std::string token;
385 while (std::getline(ss, token, ',')) {
386 const size_t start = token.find_first_not_of(" \t");
387 const size_t end = token.find_last_not_of(" \t");
388 if (start == std::string::npos) {
389 continue;
390 }
391 try {
392 out.push_back(std::stod(token.substr(start, end - start + 1)));
393 } catch (const std::exception &) {
394 throw std::invalid_argument(
395 "[Instanton] temperatures must be comma-separated kelvin "
396 "values, not " +
397 token);
398 }
399 }
400 return out;
401}
402
403// Path-integral keys live on [Dynamics] in an ini file and under Thermostat
404// in the JSON writer. Either object may carry them. The damping time is
405// stored in femtoseconds; from_json converts it once every object is read.
406static void readPathIntegralKeys(const json &s, Parameters &p) {
408 JSON_OPT(s, "path_beads", th.path_beads);
409 if (s.contains("path_springs")) {
410 th.path_springs = lowerCopy(s.at("path_springs").get<std::string>());
411 if (th.path_springs != "trotter" && th.path_springs != "eco") {
412 throw std::invalid_argument(
413 "[Dynamics] path_springs must be trotter or eco, not " +
414 th.path_springs);
415 }
416 }
417 JSON_OPT(s, "path_eco_omega_max", th.path_eco_omega_max);
418 JSON_OPT(s, "path_gle_file", th.path_gle_file);
419 JSON_OPT(s, "path_pile_tau", th.path_pile_tau_input);
420 JSON_OPT(s, "path_pile_scale", th.path_pile_scale);
421 if (s.contains("path_seed")) {
422 const long seed = s.at("path_seed").get<long>();
423 if (seed < 0) {
424 throw std::invalid_argument("[Dynamics] path_seed must be non-negative");
425 }
426 th.path_seed = static_cast<std::uint64_t>(seed);
427 }
428}
429
430static void readBathKeys(const json &s, Parameters &p) {
432 if (s.contains("thermostat")) {
433 th.kind = lowerCopy(s.at("thermostat").get<std::string>());
434 }
435 if (s.contains("kind")) {
436 th.kind = lowerCopy(s.at("kind").get<std::string>());
437 }
438 JSON_OPT(s, "andersen_alpha", th.andersen_alpha);
439 JSON_OPT(s, "andersen_collision_period", th.andersen_tcol_input);
440 JSON_OPT(s, "nose_mass", th.nose_mass);
441 JSON_OPT(s, "langevin_friction", th.langevin_friction_input);
443}
444
445void from_json(const json &j, Parameters &p) {
446 // [Main]
447 if (j.contains("Main")) {
448 auto &m = j.at("Main");
449 if (m.contains("job"))
451 m.at("job"), ParametersLoadAccess::main_options(p).job);
452 JSON_OPT(m, "random_seed",
454 JSON_OPT(m, "temperature",
456 JSON_OPT(m, "quiet", ParametersLoadAccess::main_options(p).quiet);
457 JSON_OPT(m, "write_log", ParametersLoadAccess::main_options(p).writeLog);
458 JSON_OPT(m, "checkpoint", ParametersLoadAccess::main_options(p).checkpoint);
459 JSON_OPT(m, "ini_filename",
461 JSON_OPT(m, "con_filename",
463 JSON_OPT(m, "finite_difference",
464 ParametersLoadAccess::main_options(p).finiteDifference);
465 JSON_OPT(m, "max_force_calls",
466 ParametersLoadAccess::main_options(p).maxForceCalls);
467 JSON_OPT(m, "remove_net_force",
468 ParametersLoadAccess::main_options(p).removeNetForce);
469 JSON_OPT(m, "write_con_forces",
470 ParametersLoadAccess::main_options(p).writeConForces);
471 }
472
473 // [Potential]
474 if (j.contains("Potential")) {
475 auto &s = j.at("Potential");
476 if (s.contains("potential"))
478 enum_from_json(s.at("potential"),
480 JSON_OPT(s, "mpi_poll_period",
482 JSON_OPT(s, "lammps_logging",
484 JSON_OPT(s, "lammps_threads",
486 JSON_OPT(s, "emt_rasmussen",
488 JSON_OPT(s, "log_potential",
490 JSON_OPT(s, "ext_pot_path",
492 JSON_OPT(s, "potentials_path",
494 }
495
496 // [Structure Comparison]
497 if (j.contains("Structure Comparison")) {
498 auto &s = j.at("Structure Comparison");
499 JSON_OPT(s, "distance_difference",
501 .distance_difference);
502 JSON_OPT(
503 s, "neighbor_cutoff",
505 JSON_OPT(
506 s, "check_rotation",
508 JSON_OPT(s, "indistinguishable_atoms",
510 .indistinguishable_atoms);
511 JSON_OPT(s, "energy_difference",
513 .energy_difference);
514 JSON_OPT(s, "remove_translation",
516 .remove_translation);
517 }
518
519 // [Optimizer]
520 if (j.contains("Optimizer")) {
521 auto &s = j.at("Optimizer");
522 if (s.contains("opt_method"))
524 enum_from_json(s.at("opt_method"),
526 JSON_OPT(s, "convergence_metric",
527 ParametersLoadAccess::optimizer_options(p).convergence_metric);
528 for (char &c :
529 ParametersLoadAccess::optimizer_options(p).convergence_metric) {
530 c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
531 }
533 ParametersLoadAccess::optimizer_options(p).convergence_metric)) {
535 std::string(*label);
536 } else {
537 throw std::invalid_argument(std::format(
538 "unknown convergence_metric: {}",
539 ParametersLoadAccess::optimizer_options(p).convergence_metric));
540 }
541 JSON_OPT(s, "max_iterations",
543 JSON_OPT(s, "max_move",
545 JSON_OPT(s, "converged_force",
546 ParametersLoadAccess::optimizer_options(p).converged_force);
547 JSON_OPT(s, "time_step",
548 ParametersLoadAccess::optimizer_options(p).time_step_input);
549 JSON_OPT(s, "max_time_step",
550 ParametersLoadAccess::optimizer_options(p).max_time_step_input);
551 if (s.contains("LBFGS")) {
552 auto &l = s.at("LBFGS");
553 JSON_OPT(l, "memory",
555 JSON_OPT(
556 l, "inverse_curvature",
557 ParametersLoadAccess::optimizer_options(p).lbfgs.inverse_curvature);
558 JSON_OPT(l, "max_inverse_curvature",
560 .lbfgs.max_inverse_curvature);
561 JSON_OPT(l, "auto_scale",
562 ParametersLoadAccess::optimizer_options(p).lbfgs.auto_scale);
563 JSON_OPT(l, "angle_reset",
564 ParametersLoadAccess::optimizer_options(p).lbfgs.angle_reset);
565 JSON_OPT(l, "distance_reset",
566 ParametersLoadAccess::optimizer_options(p).lbfgs.distance_reset);
567 JSON_OPT(l, "curvature",
568 ParametersLoadAccess::optimizer_options(p).lbfgs.curvature);
569 JSON_OPT(l, "project_rigid",
570 ParametersLoadAccess::optimizer_options(p).lbfgs.project_rigid);
571 JSON_OPT(l, "secant",
573 JSON_OPT(l, "precon",
575 JSON_OPT(l, "step",
578 JSON_OPT(l, "accept",
580 JSON_OPT(l, "extra_updates",
581 ParametersLoadAccess::optimizer_options(p).lbfgs.extra_updates);
582 JSON_OPT(l, "cautious_eps",
583 ParametersLoadAccess::optimizer_options(p).lbfgs.cautious_eps);
584 JSON_OPT(l, "cautious_alpha",
585 ParametersLoadAccess::optimizer_options(p).lbfgs.cautious_alpha);
586 JSON_OPT(l, "precon_A",
588 JSON_OPT(l, "precon_mu",
589 ParametersLoadAccess::optimizer_options(p).lbfgs.precon_mu);
590 JSON_OPT(l, "precon_rcut",
591 ParametersLoadAccess::optimizer_options(p).lbfgs.precon_rcut);
592 }
593 if (s.contains("Xtsci")) {
594 auto &x = s.at("Xtsci");
595 JSON_OPT(x, "method",
597 }
598 JSON_OPT(s, "xtsci_method",
600 }
601
602 // [Dynamics] Thermostat names on this object match the ini file.
603 // A later Thermostat object overrides the same keys.
604 if (j.contains("Dynamics")) {
605 auto &s = j.at("Dynamics");
606 JSON_OPT(s, "time_step",
607 ParametersLoadAccess::dynamics_options(p).time_step_input);
608 JSON_OPT(s, "time", ParametersLoadAccess::dynamics_options(p).time_input);
609 readBathKeys(s, p);
610 }
611
612 // [Thermostat]
613 if (j.contains("Thermostat")) {
614 readBathKeys(j.at("Thermostat"), p);
615 }
616
617 // [Nudged Elastic Band]
618 if (j.contains("Nudged Elastic Band")) {
619 auto &s = j.at("Nudged Elastic Band");
620 JSON_OPT(s, "images", ParametersLoadAccess::neb_options(p).image_count);
621 JSON_OPT(s, "max_iterations",
622 ParametersLoadAccess::neb_options(p).max_iterations);
623 if (s.contains("opt_method"))
625 s.at("opt_method"), ParametersLoadAccess::neb_options(p).opt_method);
626 JSON_OPT(s, "converged_force",
627 ParametersLoadAccess::neb_options(p).force_tolerance);
628 JSON_OPT(s, "solid_state",
629 ParametersLoadAccess::neb_options(p).solid_state.enabled);
630 JSON_OPT(s, "solid_state_weight",
631 ParametersLoadAccess::neb_options(p).solid_state.weight);
632 JSON_OPT(s, "solid_state_pressure",
633 ParametersLoadAccess::neb_options(p).solid_state.pressure);
634 if (s.contains("spring")) {
635 auto &sp = s.at("spring");
636 JSON_OPT(sp, "constant",
637 ParametersLoadAccess::neb_options(p).spring.constant);
638 JSON_OPT(sp, "elastic_band",
639 ParametersLoadAccess::neb_options(p).spring.use_elastic_band);
640 JSON_OPT(sp, "doubly_nudged",
641 ParametersLoadAccess::neb_options(p).spring.doubly_nudged);
642 }
643 if (s.contains("climbing_image")) {
644 auto &ci = s.at("climbing_image");
645 JSON_OPT(ci, "enabled",
646 ParametersLoadAccess::neb_options(p).climbing_image.enabled);
647 JSON_OPT(
648 ci, "converged_only",
649 ParametersLoadAccess::neb_options(p).climbing_image.converged_only);
650 JSON_OPT(ci, "band_slack",
651 ParametersLoadAccess::neb_options(p).climbing_image.band_slack);
652 }
653 if (s.contains("zoom")) {
654 auto &z = s.at("zoom");
656 JSON_OPT(z, "enabled", zoom.enabled);
657 JSON_OPT(z, "alpha", zoom.alpha);
658 JSON_OPT(z, "offset", zoom.offset);
659 if (z.contains("mode")) {
660 zoom.mode = enum_from_json(z.at("mode"), zoom.mode);
661 }
662 JSON_OPT(z, "activation_threshold", zoom.activation_threshold);
663 if (z.contains("interpolation")) {
664 zoom.interpolation =
665 enum_from_json(z.at("interpolation"), zoom.interpolation);
666 }
667 JSON_OPT(z, "stability_count", zoom.stability_count);
668 JSON_OPT(z, "max_iterations", zoom.max_iterations);
669 }
670 }
671
672 // [Dimer]
673 if (j.contains("Dimer")) {
674 auto &s = j.at("Dimer");
675 JSON_OPT(s, "rotation_angle",
676 ParametersLoadAccess::dimer_options(p).rotation_angle);
677 JSON_OPT(s, "improved", ParametersLoadAccess::dimer_options(p).improved);
678 JSON_OPT(s, "converged_angle",
679 ParametersLoadAccess::dimer_options(p).converged_angle);
680 JSON_OPT(s, "max_iterations",
681 ParametersLoadAccess::dimer_options(p).max_iterations);
682 if (s.contains("opt_method"))
684 enum_from_json(s.at("opt_method"),
686 if (s.contains("rotation_backend"))
688 s.at("rotation_backend"),
689 ParametersLoadAccess::dimer_options(p).rotation_backend);
690 JSON_OPT(s, "lor_residual_tol",
691 ParametersLoadAccess::dimer_options(p).lor_residual_tol);
692 }
693
694 // [Saddle Search]
695 if (j.contains("Saddle Search")) {
696 auto &s = j.at("Saddle Search");
697 JSON_OPT(s, "method",
699 JSON_OPT(s, "min_mode_method",
701 JSON_OPT(s, "max_energy",
703 JSON_OPT(s, "max_iterations",
705 JSON_OPT(s, "displace_magnitude",
706 ParametersLoadAccess::saddle_search_options(p).displace_magnitude);
707 JSON_OPT(s, "displace_radius",
709 }
710
711 // [Serve]
712 if (j.contains("Serve")) {
713 auto &s = j.at("Serve");
715 if (s.contains("port"))
717 s.at("port").get<uint16_t>();
718 if (s.contains("replicas"))
720 s.at("replicas").get<size_t>();
721 if (s.contains("gateway_port"))
723 s.at("gateway_port").get<uint16_t>();
724 JSON_OPT(s, "endpoints", ParametersLoadAccess::serve_options(p).endpoints);
725 }
726
727 // [Debug]
728 if (j.contains("Debug")) {
729 auto &s = j.at("Debug");
730 JSON_OPT(s, "write_movies",
732 JSON_OPT(s, "write_movies_interval",
733 ParametersLoadAccess::debug_options(p).write_movies_interval);
734 JSON_OPT(s, "write_deprecated_outs",
735 ParametersLoadAccess::debug_options(p).write_deprecated_outs);
736 }
737
738 // [Hessian] phva_atoms wins over the legacy atom_list, as in the ini.
739 if (j.contains("Hessian")) {
740 auto &s = j.at("Hessian");
742 if (s.contains("phva_atoms")) {
743 h.phva_atoms = lowerCopy(s.at("phva_atoms").get<std::string>());
744 } else if (s.contains("atom_list")) {
745 h.phva_atoms = lowerCopy(s.at("atom_list").get<std::string>());
746 }
747 JSON_OPT(s, "zero_freq_value", h.zero_freq_value);
748 if (s.contains("fd_scheme")) {
749 h.fd_scheme = lowerCopy(s.at("fd_scheme").get<std::string>());
750 }
751 JSON_OPT(s, "resume", h.resume);
752 JSON_OPT(s, "checkpoint_path", h.checkpoint_path);
753 JSON_OPT(s, "write_modes", h.write_modes);
754 }
755
756 // [Instanton]
757 if (j.contains("Instanton")) {
758 auto &s = j.at("Instanton");
760 JSON_OPT(s, "mode", o.mode);
761 JSON_OPT(s, "reactant_filename", o.reactant_filename);
762 JSON_OPT(s, "product_filename", o.product_filename);
763 JSON_OPT(s, "initial_path", o.initial_path);
764 JSON_OPT(s, "beads", o.beads);
765 JSON_OPT(s, "beta_hbar_omega", o.beta_hbar_omega);
766 JSON_OPT(s, "max_iterations", o.max_iterations);
767 JSON_OPT(s, "force_tolerance", o.force_tolerance);
768 JSON_OPT(s, "hessian_stride", o.hessian_stride);
769 JSON_OPT(s, "saddle_filename", o.saddle_filename);
770 JSON_OPT(s, "temperature", o.temperature);
771 if (s.contains("temperatures")) {
772 o.temperatures = temperaturesFromJson(s.at("temperatures"));
773 }
774 JSON_OPT(s, "half_ring", o.half_ring);
775 if (s.contains("initial_hessians")) {
776 o.initial_hessians = s.at("initial_hessians").get<std::string>();
777 }
778 if (o.initial_hessians != "saddle" &&
779 o.initial_hessians != "finite_difference") {
780 throw std::invalid_argument(
781 "[Instanton] initial_hessians must be saddle or "
782 "finite_difference, not " +
783 o.initial_hessians);
784 }
785 JSON_OPT(s, "energy_shift", o.energy_shift);
786 JSON_OPT(s, "bead_ladder", o.bead_ladder);
787 JSON_OPT(s, "hessian_final", o.hessian_final);
788 if (s.contains("springs")) {
789 o.springs = lowerCopy(s.at("springs").get<std::string>());
790 }
791 if (o.springs != "trotter" && o.springs != "eco") {
792 throw std::invalid_argument(
793 "[Instanton] springs must be trotter or eco, not " + o.springs);
794 }
795 if (o.mode != "splitting" && o.mode != "rate") {
796 throw std::invalid_argument("[Instanton] mode must be splitting or "
797 "rate, not " +
798 o.mode);
799 }
800 JSON_OPT(s, "pi_planes", o.pi_planes);
801 JSON_OPT(s, "pi_beads", o.pi_beads);
802 JSON_OPT(s, "pi_equilibration_steps", o.pi_equilibration_steps);
803 JSON_OPT(s, "pi_sampling_steps", o.pi_sampling_steps);
804 JSON_OPT(s, "pi_time_step", o.pi_time_step);
805 if (s.contains("pi_thermostat")) {
806 o.pi_thermostat = lowerCopy(s.at("pi_thermostat").get<std::string>());
807 }
808 JSON_OPT(s, "pi_gle_file", o.pi_gle_file);
809 JSON_OPT(s, "pi_pile_tau", o.pi_pile_tau);
810 JSON_OPT(s, "pi_pile_scale", o.pi_pile_scale);
811 JSON_OPT(s, "pi_seed", o.pi_seed);
812 if (s.contains("pi_direction")) {
813 o.pi_direction = lowerCopy(s.at("pi_direction").get<std::string>());
814 }
815 JSON_OPT(s, "pi_reactant_extent", o.pi_reactant_extent);
816 JSON_OPT(s, "pi_recrossing_parents", o.pi_recrossing_parents);
817 JSON_OPT(s, "pi_recrossing_children", o.pi_recrossing_children);
818 JSON_OPT(s, "pi_recrossing_time", o.pi_recrossing_time);
819 JSON_OPT(s, "pi_recrossing_spacing", o.pi_recrossing_spacing);
821 }
822
823 // The ini loader derives these from the femtosecond inputs whether or
824 // not the keys are present; validate_and_link does not.
825 {
827 const double timeUnit = ParametersLoadAccess::constants(p).timeUnit;
828 th.andersen_tcol = timeUnit > 0.0 ? th.andersen_tcol_input / timeUnit : 0.0;
829 th.path_pile_tau = timeUnit > 0.0 ? th.path_pile_tau_input / timeUnit : 0.0;
830 }
831
832 // Resolve computed fields
834}
835
836int load_json(std::string_view json_str, Parameters &params) {
837 try {
838 auto j = json::parse(json_str);
839 from_json(j, params);
840 return 0;
841 } catch (const json::exception &e) {
842 return 1;
843 } catch (const std::invalid_argument &) {
844 return 1;
845 }
846}
847
848} // namespace eonc::config
#define JSON_OPT(j, key, target)
nlohmann::json json
static E enum_from_json(const json &j, E fallback)
static json enum_to_json(E val)
void from_json(const json &j, Parameters &p)
Deserialize JSON into Parameters, then resolve computed fields.
static std::string lowerCopy(std::string value)
static void readPathIntegralKeys(const json &s, Parameters &p)
int load_json(std::string_view json_str, Parameters &params)
Load Parameters from a JSON string. Returns 0 on success.
static void readBathKeys(const json &s, Parameters &p)
json to_json(const Parameters &p)
Serialize all Parameters to JSON.
void validate_and_link(Parameters &params)
Resolve cross-group defaults and time unit conversions.
static std::vector< double > temperaturesFromJson(const json &value)
std::optional< std::string_view > convergenceMetricLabel(std::string_view metric)
Display label for a force-convergence metric, or nullopt when the name is none of the four the optimi...
void validateOptions(const instanton_options_t &o)
Throws std::invalid_argument on an inconsistent [Instanton] pi_* key.
Definition PIQTSTJob.cpp:31
static lanczos_options_t & lanczos_options(Parameters &p)
static thermostat_options_t & thermostat_options(Parameters &p)
static dimer_options_t & dimer_options(Parameters &p)
static constants_t & constants(Parameters &p)
static debug_options_t & debug_options(Parameters &p)
static hessian_options_t & hessian_options(Parameters &p)
static instanton_options_t & instanton_options(Parameters &p)
static saddle_search_options_t & saddle_search_options(Parameters &p)
static dynamics_options_t & dynamics_options(Parameters &p)
static structure_comparison_options_t & structure_comparison_options(Parameters &p)
static main_options_t & main_options(Parameters &p)
static prefactor_options_t & prefactor_options(Parameters &p)
static neb_options_t & neb_options(Parameters &p)
static optimizer_options_t & optimizer_options(Parameters &p)
static serve_options_t & serve_options(Parameters &p)
static davidson_options_t & davidson_options(Parameters &p)
static potential_options_t & potential_options(Parameters &p)
bool diagonal_preconditioner
Heuristic | (H v)_i / v_i | preconditioner (not true diag(H); off by default).
std::string phva_atoms
PHVA mobile/active set for the Ritz space; "All" = all free atoms.
DimerRotationBackend rotation_backend
bool writeConForces
Opt-in "Forces of Component" sections in written .con frames (enables force+energy co-load on restart...
double band_slack
When converged_only is on, the rest of the band may still be this many times the force tolerance.
double pressure
Hydrostatic pressure in eV/Angstrom^3.
struct eonc::neb_options_t::climbing_image_options_t climbing_image
struct eonc::neb_options_t::zoom_options_t zoom
struct eonc::neb_options_t::spring_options_t spring
struct eonc::neb_options_t::solid_state_options_t solid_state
struct eonc::optimizer_options_t::xtsci_t xtsci
struct eonc::optimizer_options_t::lbfgs_t lbfgs