Test seam: injected resource, no process-default libartn load.
85 {
86 const int nat =
matter->numberOfAtoms();
87
88 if (
mode.rows() != nat ||
mode.cols() != 3) {
89 mode = AtomMatrix::Zero(nat, 3);
90 }
91
92
93
96 AtomMatrix displacement = AtomMatrix::Zero(nat, 3);
97
98
99
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);
104
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);
112 }
113 int dim_mode[2] = {3, nat};
114
115
116
117
118 std::lock_guard<std::mutex> lock(res.library_mutex);
119 {
120 try {
121 res.require_loaded();
122 } catch (const std::exception &e) {
123 QUILL_LOG_ERROR(
log,
"ARTn library not available: {}", e.what());
126 }
127
128 res.get_create_fn()();
129
130
131 const char *units = "lammps/metal";
132 int size0 = 0;
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 {}",
136 result_units);
137 }
138
139
140 double push_step =
params.artn_options().push_step_size;
141 int result_push =
142 res.get_set_param_fn()("push_step_size", 0, &size0, &push_step);
143 if (result_push != 0) {
144 QUILL_LOG_ERROR(
log,
"set_param(push_step_size) failed with code {}",
145 result_push);
146 }
147
148 double force_thr =
params.artn_options().force_threshold;
149 int result_force =
150 res.get_set_param_fn()("forc_thr", 0, &size0, &force_thr);
151 if (result_force != 0) {
152 QUILL_LOG_ERROR(
log,
"set_param(forc_thr) failed with code {}",
153 result_force);
154 }
155
156
157
158
159
160
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);
165 res.get_destroy_fn()();
168 }
169 int result_filin =
170 res.get_set_param_fn()("filin", 0, &size0, filin.c_str());
171 if (result_filin != 0) {
172 QUILL_LOG_ERROR(
log,
"set_param(filin) failed with code {}",
173 result_filin);
174 }
175 }
176
177 int verbosity = 3;
178 int result_verbose =
179 res.get_set_param_fn()("verbose", 0, &size0, &verbosity);
180 if (result_verbose != 0) {
181 QUILL_LOG_ERROR(
log,
"set_param(verbose) failed with code {}",
182 result_verbose);
183 }
184
185
186
187
188
189
190 if (
params.artn_options().ninit >= 0) {
191 int ninit =
params.artn_options().ninit;
192 int result_ninit = res.get_set_param_fn()("ninit", 0, &size0, &ninit);
193 if (result_ninit != 0) {
194 QUILL_LOG_ERROR(
log,
"set_param(ninit) failed with code {}",
195 result_ninit);
196 }
197 }
198
199
200
201
202 if (
params.artn_options().nperp_limitation !=
"default") {
203
204
205
206 std::vector<int> nperp_vals;
207 if (!parse_nperp_limitation(
params.artn_options().nperp_limitation,
208 nperp_vals)) {
210 "artn_options.nperp_limitation '{}' is not a "
211 "comma-separated integer list",
212 params.artn_options().nperp_limitation);
213 res.get_destroy_fn()();
216 }
217 if (!nperp_vals.empty()) {
218 int nperp_size = static_cast<int>(nperp_vals.size());
219 int result_nperp = res.get_set_param_fn()(
220 "nperp_limitation", 1, &nperp_size, nperp_vals.data());
221 if (result_nperp != 0) {
222 QUILL_LOG_WARNING(
log,
"set_param(nperp_limitation) failed: {}",
223 result_nperp);
224 }
225 }
226 }
227
228
229
230 if (
params.artn_options().lanczos_min_size >= 0) {
231 int lms =
params.artn_options().lanczos_min_size;
232 res.get_set_param_fn()("lanczos_min_size", 0, &size0, &lms);
233 }
234
235
236 if (
params.artn_options().nsmooth >= 0) {
237 int ns =
params.artn_options().nsmooth;
238 res.get_set_param_fn()("nsmooth", 0, &size0, &ns);
239 }
240
241
242
243
244
245
246
247 if (
params.artn_options().nnewchance >= 0) {
248 int nnc =
params.artn_options().nnewchance;
249 res.get_set_param_fn()("nnewchance", 0, &size0, &nnc);
250 }
251
252
253 bool cerr = false;
254 res.get_setup_fn()(nat, &cerr);
255 if (cerr) {
256 QUILL_LOG_ERROR(
log,
"ARTn setup failed (nat={})", nat);
257 res.get_destroy_fn()();
260 }
261
262
263
264
265
266 if (mode_norm > 1e-10) {
267 int result =
268 res.get_set_param_fn()("push_init", 2, dim_mode, mode_fort.data());
269 if (result != 0) {
270 QUILL_LOG_WARNING(
log,
"set_param(push_init) failed with code {}",
271 result);
272 }
273 }
274 }
275
276
277 std::vector<int> ityp(nat);
278 std::vector<int> if_pos(3 * nat, 1);
279 double box_f[9];
280 bool lconv = false;
281
282 for (int i = 0; i < nat; i++) {
283 if (
matter->getFixed(i)) {
284 Eigen::Map<Eigen::Vector3i>(&if_pos[i * 3]).setZero();
285 }
286 ityp[i] =
matter->getAtomicNr(i);
287 }
288
289
292
293 int maxIter =
params.artn_options().max_iterations;
294
295
296
297
300
301
302
303 double energy =
matter->getPotentialEnergy();
304 forces =
matter->getForces();
306
307
308
309
310
311
312 res.get_artn_step_fn()(nat, energy, force_map.data(), ityp.data(),
313 pos_map.data(), box_f, if_pos.data(),
314 disp_map.data(), &lconv);
315
316 if (!lconv) {
317
318
319 positions += displacement;
320 matter->setPositions(positions);
322 }
323 }
324
325
326 if (lconv) {
327
328
329
330
331 int artn_err = 0;
332 std::string artn_err_msg;
333 if (auto *get_error_fn_ = res.get_get_error_fn()) {
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));
338 std::free(cmsg);
339 }
340 } else {
341 bool *has_error_ptr = nullptr;
342 int result_has_error = res.get_get_data_fn()(
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 {}",
346 result_has_error);
347 } else if (has_error_ptr) {
348 artn_err = *has_error_ptr ? 1 : 0;
349 std::free(has_error_ptr);
350 }
351 }
352 bool has_error = artn_err != 0;
353
354 bool has_sad = false;
355 bool *has_sad_ptr = nullptr;
356 int result_has_sad = res.get_get_data_fn()(
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 {}",
360 result_has_sad);
361 } else if (has_sad_ptr) {
362 has_sad = *has_sad_ptr;
363 std::free(has_sad_ptr);
364 }
365
366 if (has_error && !artn_err_msg.empty()) {
367 QUILL_LOG_WARNING(
log,
"pARTn reported error {}: {}", artn_err,
368 artn_err_msg);
369 }
370
371
372 if (has_sad) {
373 QUILL_LOG_INFO(
375 "ARTn found saddle after {} iterations (has_error={}, has_sad={})",
377
378
379
380
381 double *tau_sad_ptr = nullptr;
382 int result_tau_sad = res.get_get_data_fn()(
383 "tau_sad", reinterpret_cast<void **>(&tau_sad_ptr));
384
385
386 if (result_tau_sad != 0 || tau_sad_ptr == nullptr) {
387 QUILL_LOG_WARNING(
388 log,
"Failed to retrieve tau_sad (result={}, ptr_valid={})",
389 result_tau_sad, tau_sad_ptr != nullptr);
390 std::free(tau_sad_ptr);
392 res.get_destroy_fn()();
394 }
396 std::vector<double>(tau_sad_ptr, tau_sad_ptr + 3 * nat), nat));
397 std::free(tau_sad_ptr);
398
399
400 double *eigval_ptr = nullptr;
401 int result_eigval = res.get_get_data_fn()(
402 "eigval_sad", reinterpret_cast<void **>(&eigval_ptr));
403 if (result_eigval == 0 && eigval_ptr) {
405 std::free(eigval_ptr);
406 } else {
407 QUILL_LOG_WARNING(
408 log,
"Failed to retrieve eigenvalue (result={}, ptr_valid={})",
409 result_eigval, eigval_ptr != nullptr);
411 std::numeric_limits<double>::quiet_NaN();
412
413 }
414
415
416
417
418
419 double *evec_ptr = nullptr;
420 int result_evec = res.get_get_data_fn()(
421 "eigen_sad", reinterpret_cast<void **>(&evec_ptr));
422 if (result_evec == 0 && evec_ptr) {
423
425 std::vector<double>(evec_ptr, evec_ptr + 3 * nat), nat);
426
427
428 std::free(evec_ptr);
429 } else {
430 QUILL_LOG_ERROR(
431 log,
"Failed to retrieve eigenvector (result={}, ptr_valid={})",
432 result_evec, evec_ptr != nullptr);
433
435 }
436
438 res.get_destroy_fn()();
440 }
441
442
443 QUILL_LOG_WARNING(
444 log,
"ARTn stopped after {} iterations (has_error={}, has_sad={})",
447 res.get_destroy_fn()();
449 }
450
451 QUILL_LOG_WARNING(
log,
"ARTn did not converge after {} iterations",
454
455
456 res.get_destroy_fn()();
458}
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Eigen::Matrix< double, 3, Eigen::Dynamic, Eigen::ColMajor > AtomMatrixF
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
const Parameters & params
AtomMatrix from_fortran_layout_vector(const std::vector< double > &flat_colmajor, int nat)
Reconstruct AtomMatrix from a flat column-major vector (e.g.
void lattice_rows_to_fortran_box(const Matrix3d &cell, double *box)
Pack an eOn cell for pARTn artn_step.