86 const int nat =
matter->numberOfAtoms();
88 if (
mode.rows() != nat ||
mode.cols() != 3) {
89 mode = AtomMatrix::Zero(nat, 3);
96 AtomMatrix displacement = AtomMatrix::Zero(nat, 3);
100 Eigen::Map<AtomMatrixF> pos_map(positions.data(), 3, nat);
101 Eigen::Map<AtomMatrixF> force_map(forces.data(), 3, nat);
102 Eigen::Map<AtomMatrixF> disp_map(displacement.data(), 3, nat);
103 Eigen::Map<AtomMatrixF> mode_map(
mode.data(), 3, nat);
105 const double push_step =
params.artn_options().push_step_size;
106 const double mode_norm =
mode.norm();
108 if (mode_norm > 1e-10) {
109 mode_fort = mode_map;
110 Eigen::Map<VectorXd> mode_vec_map(mode_fort.data(), mode_fort.size());
111 mode_vec_map *= (push_step / mode_norm);
113 int dim_mode[2] = {3, nat};
122 }
catch (
const std::exception &e) {
123 QUILL_LOG_ERROR(
log,
"ARTn library not available: {}", e.what());
131 const char *units =
"lammps/metal";
133 int result_units = res.
get_set_param_fn()(
"engine_units", 0, &size0, units);
134 if (result_units != 0) {
135 QUILL_LOG_ERROR(
log,
"set_param(engine_units) failed with code {}",
140 double push_step =
params.artn_options().push_step_size;
143 if (result_push != 0) {
144 QUILL_LOG_ERROR(
log,
"set_param(push_step_size) failed with code {}",
148 double force_thr =
params.artn_options().force_threshold;
151 if (result_force != 0) {
152 QUILL_LOG_ERROR(
log,
"set_param(forc_thr) failed with code {}",
161 const std::string &filin =
params.artn_options().filin;
162 if (!filin.empty()) {
163 if (!std::filesystem::exists(filin)) {
164 QUILL_LOG_ERROR(
log,
"artn_options.filin '{}' does not exist", filin);
171 if (result_filin != 0) {
172 QUILL_LOG_ERROR(
log,
"set_param(filin) failed with code {}",
180 if (result_verbose != 0) {
181 QUILL_LOG_ERROR(
log,
"set_param(verbose) failed with code {}",
190 if (
params.artn_options().ninit >= 0) {
191 int ninit =
params.artn_options().ninit;
193 if (result_ninit != 0) {
194 QUILL_LOG_ERROR(
log,
"set_param(ninit) failed with code {}",
202 if (
params.artn_options().nperp_limitation !=
"default") {
206 std::vector<int> nperp_vals;
207 if (!parse_nperp_limitation(
params.artn_options().nperp_limitation,
210 "artn_options.nperp_limitation '{}' is not a "
211 "comma-separated integer list",
212 params.artn_options().nperp_limitation);
217 if (!nperp_vals.empty()) {
218 int nperp_size =
static_cast<int>(nperp_vals.size());
220 "nperp_limitation", 1, &nperp_size, nperp_vals.data());
221 if (result_nperp != 0) {
222 QUILL_LOG_WARNING(
log,
"set_param(nperp_limitation) failed: {}",
230 if (
params.artn_options().lanczos_min_size >= 0) {
231 int lms =
params.artn_options().lanczos_min_size;
236 if (
params.artn_options().nsmooth >= 0) {
237 int ns =
params.artn_options().nsmooth;
247 if (
params.artn_options().nnewchance >= 0) {
248 int nnc =
params.artn_options().nnewchance;
256 QUILL_LOG_ERROR(
log,
"ARTn setup failed (nat={})", nat);
266 if (mode_norm > 1e-10) {
270 QUILL_LOG_WARNING(
log,
"set_param(push_init) failed with code {}",
277 std::vector<int> ityp(nat);
278 std::vector<int> if_pos(3 * nat, 1);
282 for (
int i = 0; i < nat; i++) {
283 if (
matter->getFixed(i)) {
284 Eigen::Map<Eigen::Vector3i>(&if_pos[i * 3]).setZero();
286 ityp[i] =
matter->getAtomicNr(i);
293 int maxIter =
params.artn_options().max_iterations;
303 double energy =
matter->getPotentialEnergy();
304 forces =
matter->getForces();
313 pos_map.data(), box_f, if_pos.data(),
314 disp_map.data(), &lconv);
319 positions += displacement;
320 matter->setPositions(positions);
332 std::string artn_err_msg;
334 void *cmsg =
nullptr;
335 artn_err = get_error_fn_(&cmsg);
336 if (artn_err != 0 && cmsg !=
nullptr) {
337 artn_err_msg.assign(
static_cast<const char *
>(cmsg));
341 bool *has_error_ptr =
nullptr;
343 "has_error",
reinterpret_cast<void **
>(&has_error_ptr));
344 if (result_has_error != 0) {
345 QUILL_LOG_WARNING(
log,
"get_data(has_error) failed with code {}",
347 }
else if (has_error_ptr) {
348 artn_err = *has_error_ptr ? 1 : 0;
349 std::free(has_error_ptr);
352 bool has_error = artn_err != 0;
354 bool has_sad =
false;
355 bool *has_sad_ptr =
nullptr;
357 "has_sad",
reinterpret_cast<void **
>(&has_sad_ptr));
358 if (result_has_sad != 0) {
359 QUILL_LOG_WARNING(
log,
"get_data(has_sad) failed with code {}",
361 }
else if (has_sad_ptr) {
362 has_sad = *has_sad_ptr;
363 std::free(has_sad_ptr);
366 if (has_error && !artn_err_msg.empty()) {
367 QUILL_LOG_WARNING(
log,
"pARTn reported error {}: {}", artn_err,
375 "ARTn found saddle after {} iterations (has_error={}, has_sad={})",
381 double *tau_sad_ptr =
nullptr;
383 "tau_sad",
reinterpret_cast<void **
>(&tau_sad_ptr));
386 if (result_tau_sad != 0 || tau_sad_ptr ==
nullptr) {
388 log,
"Failed to retrieve tau_sad (result={}, ptr_valid={})",
389 result_tau_sad, tau_sad_ptr !=
nullptr);
390 std::free(tau_sad_ptr);
396 std::vector<double>(tau_sad_ptr, tau_sad_ptr + 3 * nat), nat));
397 std::free(tau_sad_ptr);
400 double *eigval_ptr =
nullptr;
402 "eigval_sad",
reinterpret_cast<void **
>(&eigval_ptr));
403 if (result_eigval == 0 && eigval_ptr) {
405 std::free(eigval_ptr);
408 log,
"Failed to retrieve eigenvalue (result={}, ptr_valid={})",
409 result_eigval, eigval_ptr !=
nullptr);
411 std::numeric_limits<double>::quiet_NaN();
419 double *evec_ptr =
nullptr;
421 "eigen_sad",
reinterpret_cast<void **
>(&evec_ptr));
422 if (result_evec == 0 && evec_ptr) {
425 std::vector<double>(evec_ptr, evec_ptr + 3 * nat), nat);
431 log,
"Failed to retrieve eigenvector (result={}, ptr_valid={})",
432 result_evec, evec_ptr !=
nullptr);
444 log,
"ARTn stopped after {} iterations (has_error={}, has_sad={})",
451 QUILL_LOG_WARNING(
log,
"ARTn did not converge after {} iterations",