44 const int nat =
matter->numberOfAtoms();
46 if (
mode.rows() != nat ||
mode.cols() != 3) {
47 mode = AtomMatrix::Zero(nat, 3);
54 AtomMatrix displacement = AtomMatrix::Zero(nat, 3);
58 Eigen::Map<AtomMatrixF> pos_map(positions.data(), 3, nat);
59 Eigen::Map<AtomMatrixF> force_map(forces.data(), 3, nat);
60 Eigen::Map<AtomMatrixF> disp_map(displacement.data(), 3, nat);
61 Eigen::Map<AtomMatrixF> mode_map(
mode.data(), 3, nat);
63 const double push_step =
params.artn_options.push_step_size;
64 const double mode_norm =
mode.norm();
66 if (mode_norm > 1e-10) {
68 Eigen::Map<VectorXd> mode_vec_map(mode_fort.data(), mode_fort.size());
69 mode_vec_map *= (push_step / mode_norm);
71 int dim_mode[2] = {3, nat};
78 std::lock_guard<std::mutex> lock(res.library_mutex);
82 }
catch (
const std::exception &e) {
83 QUILL_LOG_ERROR(
log,
"ARTn library not available: {}", e.what());
88 res.get_create_fn()();
91 const char *units =
"lammps/metal";
93 int result_units = res.get_set_param_fn()(
"engine_units", 0, &size0, units);
94 if (result_units != 0) {
95 QUILL_LOG_ERROR(
log,
"set_param(engine_units) failed with code {}",
100 double push_step =
params.artn_options.push_step_size;
102 res.get_set_param_fn()(
"push_step_size", 0, &size0, &push_step);
103 if (result_push != 0) {
104 QUILL_LOG_ERROR(
log,
"set_param(push_step_size) failed with code {}",
108 double force_thr =
params.artn_options.force_threshold;
110 res.get_set_param_fn()(
"forc_thr", 0, &size0, &force_thr);
111 if (result_force != 0) {
112 QUILL_LOG_ERROR(
log,
"set_param(forc_thr) failed with code {}",
121 const std::string &filin =
params.artn_options.filin;
122 if (!filin.empty()) {
123 if (!std::filesystem::exists(filin)) {
124 QUILL_LOG_ERROR(
log,
"artn_options.filin '{}' does not exist", filin);
125 res.get_destroy_fn()();
130 res.get_set_param_fn()(
"filin", 0, &size0, filin.c_str());
131 if (result_filin != 0) {
132 QUILL_LOG_ERROR(
log,
"set_param(filin) failed with code {}",
139 res.get_set_param_fn()(
"verbose", 0, &size0, &verbosity);
140 if (result_verbose != 0) {
141 QUILL_LOG_ERROR(
log,
"set_param(verbose) failed with code {}",
150 if (
params.artn_options.ninit >= 0) {
151 int ninit =
params.artn_options.ninit;
152 int result_ninit = res.get_set_param_fn()(
"ninit", 0, &size0, &ninit);
153 if (result_ninit != 0) {
154 QUILL_LOG_ERROR(
log,
"set_param(ninit) failed with code {}",
162 if (
params.artn_options.nperp_limitation !=
"default") {
164 std::vector<int> nperp_vals;
165 std::istringstream ss(
params.artn_options.nperp_limitation);
167 while (std::getline(ss, token,
',')) {
168 nperp_vals.push_back(std::stoi(token));
170 if (!nperp_vals.empty()) {
171 int nperp_size =
static_cast<int>(nperp_vals.size());
172 int result_nperp = res.get_set_param_fn()(
173 "nperp_limitation", 1, &nperp_size, nperp_vals.data());
174 if (result_nperp != 0) {
175 QUILL_LOG_WARNING(
log,
"set_param(nperp_limitation) failed: {}",
183 if (
params.artn_options.lanczos_min_size >= 0) {
184 int lms =
params.artn_options.lanczos_min_size;
185 res.get_set_param_fn()(
"lanczos_min_size", 0, &size0, &lms);
189 if (
params.artn_options.nsmooth >= 0) {
190 int ns =
params.artn_options.nsmooth;
191 res.get_set_param_fn()(
"nsmooth", 0, &size0, &ns);
200 if (
params.artn_options.nnewchance >= 0) {
201 int nnc =
params.artn_options.nnewchance;
202 res.get_set_param_fn()(
"nnewchance", 0, &size0, &nnc);
207 res.get_setup_fn()(nat, &cerr);
209 QUILL_LOG_ERROR(
log,
"ARTn setup failed (nat={})", nat);
210 res.get_destroy_fn()();
219 if (mode_norm > 1e-10) {
221 res.get_set_param_fn()(
"push_init", 2, dim_mode, mode_fort.data());
223 QUILL_LOG_WARNING(
log,
"set_param(push_init) failed with code {}",
230 std::vector<int> ityp(nat);
231 std::vector<int> if_pos(3 * nat, 1);
235 for (
int i = 0; i < nat; i++) {
236 if (
matter->getFixed(i)) {
237 Eigen::Map<Eigen::Vector3i>(&if_pos[i * 3]).setZero();
239 ityp[i] =
matter->getAtomicNr(i);
244 for (
int i = 0; i < 3; i++)
245 for (
int j = 0; j < 3; j++)
246 box_f[j * 3 + i] = cell(i, j);
248 int maxIter =
params.artn_options.max_iterations;
258 double energy =
matter->getPotentialEnergy();
259 forces =
matter->getForces();
271 std::unique_lock<std::mutex> lock(res.library_mutex);
272 res.get_artn_step_fn()(nat, energy, force_map.data(), ityp.data(),
273 pos_map.data(), box_f, if_pos.data(),
274 disp_map.data(), &lconv);
280 positions += displacement;
281 matter->setPositions(positions);
288 std::lock_guard<std::mutex> lock(res.library_mutex);
295 std::string artn_err_msg;
296 if (
auto *get_error_fn_ = res.get_get_error_fn()) {
297 void *cmsg =
nullptr;
298 artn_err = get_error_fn_(&cmsg);
299 if (artn_err != 0 && cmsg !=
nullptr) {
300 artn_err_msg.assign(
static_cast<const char *
>(cmsg));
304 bool *has_error_ptr =
nullptr;
305 int result_has_error = res.get_get_data_fn()(
306 "has_error",
reinterpret_cast<void **
>(&has_error_ptr));
307 if (result_has_error != 0) {
308 QUILL_LOG_WARNING(
log,
"get_data(has_error) failed with code {}",
310 }
else if (has_error_ptr) {
311 artn_err = *has_error_ptr ? 1 : 0;
312 std::free(has_error_ptr);
315 bool has_error = artn_err != 0;
317 bool has_sad =
false;
318 bool *has_sad_ptr =
nullptr;
319 int result_has_sad = res.get_get_data_fn()(
320 "has_sad",
reinterpret_cast<void **
>(&has_sad_ptr));
321 if (result_has_sad != 0) {
322 QUILL_LOG_WARNING(
log,
"get_data(has_sad) failed with code {}",
324 }
else if (has_sad_ptr) {
325 has_sad = *has_sad_ptr;
326 std::free(has_sad_ptr);
329 if (has_error && !artn_err_msg.empty()) {
330 QUILL_LOG_WARNING(
log,
"pARTn reported error {}: {}", artn_err,
338 "ARTn found saddle after {} iterations (has_error={}, has_sad={})",
344 double *tau_sad_ptr =
nullptr;
345 int result_tau_sad = res.get_get_data_fn()(
346 "tau_sad",
reinterpret_cast<void **
>(&tau_sad_ptr));
347 if (result_tau_sad == 0 && tau_sad_ptr) {
349 std::vector<double>(tau_sad_ptr, tau_sad_ptr + 3 * nat), nat));
350 std::free(tau_sad_ptr);
353 log,
"Failed to retrieve tau_sad (result={}, ptr_valid={})",
354 result_tau_sad, tau_sad_ptr !=
nullptr);
358 double *eigval_ptr =
nullptr;
359 int result_eigval = res.get_get_data_fn()(
360 "eigval_sad",
reinterpret_cast<void **
>(&eigval_ptr));
361 if (result_eigval == 0 && eigval_ptr) {
363 std::free(eigval_ptr);
366 log,
"Failed to retrieve eigenvalue (result={}, ptr_valid={})",
367 result_eigval, eigval_ptr !=
nullptr);
369 std::numeric_limits<double>::quiet_NaN();
377 double *evec_ptr =
nullptr;
378 int result_evec = res.get_get_data_fn()(
379 "eigen_sad",
reinterpret_cast<void **
>(&evec_ptr));
380 if (result_evec == 0 && evec_ptr) {
383 std::vector<double>(evec_ptr, evec_ptr + 3 * nat), nat);
389 log,
"Failed to retrieve eigenvector (result={}, ptr_valid={})",
390 result_evec, evec_ptr !=
nullptr);
396 res.get_destroy_fn()();
402 log,
"ARTn stopped after {} iterations (has_error={}, has_sad={})",
405 res.get_destroy_fn()();
409 QUILL_LOG_WARNING(
log,
"ARTn did not converge after {} iterations",
415 std::lock_guard<std::mutex> lock(res.library_mutex);
416 res.get_destroy_fn()();
421 QUILL_LOG_ERROR(
log,
"ARTn support not compiled");