forked from libAtoms/pymatnest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ns_run.py
executable file
·3999 lines (3425 loc) · 194 KB
/
ns_run.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import re, math, time, os
import pprint
import numpy as np, ase, ase.io
import ns_rng
import stacktrace
from copy import deepcopy
import pick_interconnected_clump
try:
import matscipy.neighbours
except:
pass
import collections
from traceback import print_exception
import check_memory
from ase.md.verlet import VelocityVerlet
import importlib
print_prefix=""
def usage():
""" Print help to the standard output about the usage of the code and input parameters. The current list of parameters is the following:
.. glossary::
``max_volume_per_atom=float``
| Maximum volume per atom allowed during the run.
| default: 1.0e3
``min_volume_per_atom=float``
| Minimum volume per atom allowed during the run.
| default: 1.0
``start_species=int int [ float ] [, int int [ float ] ... ]``
| MANDATORY (or start_config_file)
| Atomic number; multiplicity; [ not recommended: mass (amu) ]. Info repeated for each species, separated by commas, mass is optional and not recommended.
``start_config_file=str``
| MANDATORY (or start_species)
| Name of file to read in for atom information
| default: ''
``restart_file=path_to_file``
| File for restart configs. Mutually exclusive with ``start_*``, one is required. The file should contain the state of the walkers to continue from along with the restart iteration number. Normally such a file can be the concatenated snapshot files.
``n_walkers=int``
| MANDATORY
| Total number of walkers, i.e. the size of the live set used. It has to be the multiple of the number of processors used and it also has to be larger than the number of processors.
``n_cull=int``
| Number of walkers to kill at each NS iteration. Use of default 1 is strongly recommended.
| deafult: 1
``n_extra_walk_per_task=int``
| default: 0
``n_iter_times_fraction_killed=int``
| MANDATORY
| Number of nested sampling iteration cycles performed per walker. Thus the total number of iterations will be ``n_iter_times_fraction_killed`` / ``(n_cull/n_walkers)``. Either this or ``converge_down_to_T`` is required.
``converge_down_to_T=flot``
| MANDATORY
| temperature down to which Z(T) should be converged. Either this or ``n_iter_times_fraction_killed`` is required.
``T_estimate_finite_diff_lag=int``
| default: 1000
| Lag (in iterations) for doing d log Omega / d E finite difference derivative
``min_Emax=float``
| Termination condition based on Emax: if this value is reached, the iteration will stop.
| No default.
``out_file_prefix=str``
| String used as prefix for the different output files.
| No default.
``energy_calculator= ( ASE | lammps | internal | fortran)``
| Energy calculator.
| default: fortran
``n_extra_data=int``
| Amount of extra data per atom to pass around.
| default: 0
``KEmax_max_T=float``
| If > 0, maximum temperature for estimating KEmax. For constant *P* ensemble 3/2 P Vmax will be used if KEmax_max_T is < 0.
| default: -1
``kB=float``
| Boltzmann constant
| default: 8.6173324e-5 (eV/A)
``start_energy_ceiling_per_atom=float``
| Maximum potential energy per atom for initial configurations. P*Vmax is added to this automatically in case of NpT runs.
| default: 1.0e9
``start_energy_ceiling=float``
| DEPRECATED: use ``start_energy_ceiling_per_atom``. Maximum potential energy for initial configurations. P*Vmax is added to this automatically in case of NpT runs.
| default: 1.0e9
``random_init_max_n_tries=int``
| Maximum number of tries to create initial random atomic configuration
| default 100
``n_model_calls_expected=int``
| Total number of model calls performed during the random walk, expected by the user. The actual number of model calls performed during runtime can be different: this number is divided up among the processors in case of running parallel, and in order to make sure different processors perform about the same number of calls, it can be increased for some. (It is recommended to use n_model_calls_expected/[number of processors] > 20) Either this or the keyword ``n_model_calls`` is mandatory, but the use of this keyword is strongly recommended. If < 0, value will be set to _sum_ of each type of steps (accounting for atom_traj_len), so ``n_*_steps`` will be setting the _number_ of calls, rather than just the ratios.
| default: 0
``n_model_calls=int``
| Number of model calls performed during the random walk. This is the actual number of calls performed, ideally the program sets its value depending on n_model_calls_expected and the number of processors used. Either this or the keyword ``n_model_calls_expected`` is mandatory, though the usage of the latter is strongly recommended.
| default: 0
``do_good_load_balance=[T | F]``
| Whether to do steps in groups with good load balance
| default: F
``n_atom_steps=int``
| Ratio of atomic trajectories will be determined by ``n_atom_steps``/SUM(``n_*_steps``).
| default: 1
``atom_traj_len=int``
| Length of atomic trajectory (MD steps or MC sweeps) in each atomic type step.
| default: 8
``atom_traj_len_cost_multiplier=int``
| Multiplier for cost of an atomic step (set to 1 for MD, TE-HMC, and SP-MC with O(1) cost moves, N for naive SP-MC)
| default: 1
``break_up_atom_traj=[T | F]``
| Whether to intersperse ``n_atom_steps`` atomic sub-trajectories with other types of steps.
| default: F
``n_cell_volume_steps=int``
| Ratio of cell volume steps will be determined by ``n_cell_volume_steps``/SUM(``n_*_steps``).
| default: 1
``n_cell_shear_steps=int``
| Ratio of cell shear steps will be determined by ``n_cell_shear_steps``/SUM(``n_*_steps``).
| default: 1
``n_cell_stretch_steps=int``
| Ratio of cell stretch steps will be determined by ``n_cell_stretch_steps``/SUM(``n_*_steps``).
| default: 1
``n_swap_steps=int``
| Ratio of species swap steps will be determined by ``n_swap_steps``/SUM(``n_*_steps``). It has to be set other than zero for a multicomponent system.
| default: 0
``swap_max_cluster=int``
| Maximum size of interconnected cluster to try to swap.
| default: 1
``swap_r_cut=float``
| Cutoff radius for defining connected atoms for cluster.
| default: 2.5
``swap_cluster_probability_increment=float``
| Factor between prob. of picking increasing larger clusters.
| default: 0.75
``swap_velo=[T | F]``
| If true, swap velocities when swapping atoms, breaking coherence a bit.
| default: F
``no_swap_velo_fix_mag_alt=[T | F]``
| If true, use alternate method for correcting velocity magnitudes when not swapping velocities.
| default: F
``n_semi_grand_steps=int``
| Ratio of species type-change steps will be determined by ``n_semi_grand_steps``/SUM(``n_*_steps``). Only makes sense for a multicomponent system.
| default: 0
``semi_grand_potentials=Z1 : mu1 [, Z2 : mu2 ...]``
| Chemical potentials for semi-grand canonical species-type transmutations
| default: ''
``velo_traj_len=int``
| Number of MC steps in (optional) explicit velocity MC trajectory.
| default: 0
``random_energy_perturbation=float``
| default: 1.0e-12
``atom_algorithm=[MC | MD | GMC]``
| MANDATORY
| Use either Monte Carlo or Molecular dynamics to explore. GMC is an alias for atom_algorithm=MC, MC_atom_Galilean=True.
``MC_atom_velocities=[T | F]``
| This keyword is supported only for energy_calculator=fortran.
| default: F
``MC_atom_velocities_pre_perturb=[T | F]``
| Perturb velocities (rejection free) before MC + velocities walk.
| default: F
``MC_atom_step_size=float``
| Initial atom move step size in units of (max_volume_per_atom * N_atoms)^(1/3)
| default: 1.0
``MC_atom_step_size_max=float``
| Maximum atom step size in units of (max_volume_per_atom * N_atoms)^(1/3).
| default: 1.0
``MC_atom_uniform_rv=[T | F]``
| default: F
``MC_atom_Galilean=[T | F]``
| default: F
``GMC_no_reverse=[T | F]``
| default: T
``MD_atom_velo_pre_perturb=[T | F]``
| Perturb velocities before MD trajectory
| default: F
``MD_atom_velo_post_perturb=[T | F]``
| Perturb velocities after MD trajectory
| default: T
``MD_atom_velo_flip_accept=[T | F]``
| default: F
``MD_atom_timestep=float``
| default: 0.1 (ASE time units)
``MD_atom_timestep_max=float``
| default: 2.0 (ASE time units)
``MD_atom_energy_fuzz=float``
| Tolerance for rejecting non-energy conserving trajectories, as fraction of Kinetic Energy
| default: 1.0e-2
``MD_atom_reject_energy_violation=[ T | F ]``
| Use energy conservation violation (exceeding MD_atom_energy_fuzz * KE) to reject MD trajectories.
| default: F
``python_MD=[ T | F ]``
| Do MD using python code rather than underlying driver
| default: F
``atom_velo_rej_free_fully_randomize=[T | F]``
| If true, randomize velocities completely rather than just perturbing.
| default: F
``atom_velo_rej_free_perturb_angle=float``
| Max angle in radians for random rotations.
| default: 0.3
``MC_atom_velo_step_size=float``
| default: 50.0
``MC_atom_velo_step_size_max=float``
| default: 10000.0
``MC_atom_velo_walk_rej_free=[T | F]``
| default: T. If true, use rejection free algorithm for MC_atom_walk
``MC_cell_P=float``
| Pressure value to be used. The unit of pressure depends on both the energy calculator and on the potential model used. Note that ASE uses
| eV as energy and Angstrom as distance units everywhere, thus this case the pressure in the input have to be eV/Angstrom^3 (in case of using LAMMPS
| the lammpslib module will convert the units for lammps to whatever units are set by the potential type)
| default: 0.0
``MC_cell_flat_V_prior=[T | F]``
| Flat prior for V (use with MC_cell_P=0 and cell moves, requires reweighting configurations when analyzing)
| POORLY TESTED, DO NOT TRUST (YET).
| default: F
``MC_cell_volume_per_atom_step_size=float``
| Initial volume stepsize for volume change.
| default: 5% of the maximum allowed volume
``MC_cell_volume_per_atom_step_size_max=float``
| Maximum allowed volume step size.
| default: 50% of the maximum allowed volume
``MC_cell_volume_per_atom_prob=float``
| default: 1.0
``MC_cell_stretch_step_size=float``
| default: 0.1
``MC_cell_stretch_step_size_max=float``
| default: 1.0
``MC_cell_stretch_prob=float``
| default: 1.0
``MC_cell_shear_step_size=float``
| default: 0.1, in units of (max_volume_per_atom * N_atoms)^(1/3)
``MC_cell_shear_step_size_max=float``
| default: 1.0, in units of (max_volume_per_atom * N_atoms)^(1/3)
``MC_cell_shear_prob=float``
| default: 1.0
``MC_cell_min_aspect_ratio=float``
| Smallest allowed distance between parallel faces for cell normalised to unit volume. A higher value of ``MC_cell_min_aspect_ratio`` restricts the system to more cube-like cell shapes, while a low value allows the system to become essentially flat. In case of 64 atoms the use of ``MC_cell_min_aspect_ratio`` < 0.65 *does* effect the melting transition.
| default: 0.8
``cell_shape_equil_steps=int``
| default: 1000
``full_auto_step_sizes=[T | F]``
| If true (T), automatically calibrate all sizes by performing additional short explorations, including at the start of run. If false (F), use initial input step sizes and make small adjustments to these during the run.
| default: T
``monitor_step_interval_times_fraction_killed=float``
| Divided by ``n_cull/n_walkers`` to get actual monitoring interval in iterations, negative for only using last iteration, 0 for no monitoring
| default: 1
``adjust_step_interval_times_fraction_killed=float``
| Divided by ``n_cull/n_walkers`` to get actual step adjustment interval in iterations, negative for only using last iteration, 0 for no adjust.
| default: 1
``MC_adjust_step_factor=float``
| default: 1.1
``MC_adjust_min_rate=float``
| default: 0.25
``MC_adjust_max_rate=float``
| default: 0.75
``GMC_adjust_min_rate=float``
| default: 0.25
``GMC_adjust_max_rate=float``
| default: 0.75
``GMC_dir_perturb_angle=float``
| default: -1.0
``GMC_dir_perturb_angle_during=float``
| default: 0.0
``MD_adjust_step_factor=float``
| default: 1.1
``MD_adjust_min_rate=float``
| default: 0.5
``MD_adjust_max_rate=float``
| default: 0.95
``ASE_calc_module=str``
| MANDATORY if energy_calculator=ASE
``FORTRAN_model=str``
| MANDATORY if energy_calculator=fortran
``FORTRAN_model_params=str``
| parameters (optional) for fortran model
``LAMMPS_fix_gmc=[T | F]``
| default: F
``LAMMPS_init_cmds=str``
| MANDATORY if energy_calculator=lammps
``LAMMPS_name=str``
| '', arch name for lammps shared object file
``LAMMPS_serial=[T | F]``
| default True, lammps library is serial so do not pass COMM_SELF as a communicator
``LAMMPS_header=str``
| lammpslib.py value default, override lammpslib.py header commands for energy_calculator=lammps
``LAMMPS_header_extra=str``
| '', extra lammpslib.py header commands for energy_calculator=lammps
``LAMMPS_atom_types=str``
| MANDATORY if energy_calculator=lammps
| atomic_symbol1 lammps_type1 [, atomic_symbol2 lammps_type2, ...]
| mapping between atom species and lammps potential types
``config_file_format=str``
| File format in which configurations are printed, e.g. for trajectory and snapshot files.
| default: extxyz
``rng=( numpy | internal | rngstream )``
| Random number generator.
| default: numpy
``profile=rank_to_profile``
| default: -1
``2D=[ T | F ]``
| Perform 2D simulation. Some functionality may be limited with this option!
| The 2D area is in the xy plane, the z axis stays constant during the simulation (set its value using the keyword ``Z_cell_axis``).
| The maximum allowed volume is still defined as volume in this case!
| If constant pressure is set, the p*V term is now p*A*Z_cell_axis, thus set the MC_cell_P parameter multiplied by 1/Z_cell_axis.
| default: F
``Z_cell_axis=float``
| Only used if 2D=T. Value of the Z cell axis, perpendicular to the area in case of a 2D simulation.
| This stays constant during the simulation.
| default: 10.0
``debug=int``
| Verbosity level used in the output file. The larger its value the more info is printed.
| default: 0
``snapshot_interval=int``
| Iteration interval at which a snapshot is created: every process prints out its current walkers in extended xyz format. If it is set <=0, no snapshots will be printed except the final positions at the end of the nested sampling run. Note that when new snapshots are printed, the previous set is deleted. The snapshot files are convenient source to see how the sampling progresses, but these are also the basis to restart a sampling! When using restart, the walkers will be read from these files.
| default: 10000
``snapshot_time=int``
| Max time between snapshots in seconds
| default: 3600
``snapshot_per_parallel_task=[ T | F ]``
| Save a separate snapshot file for each parallel task. Faster I/O (in parallel), but less convenient in some cases.
| default: T
``snapshot_clean=[ T | F ]``
| If true, delete previous iteration snapshot files
| default: T
``random_initialise_pos=[ T | F ]``
| If true, randomize the initial positions
| default: T
``random_initialise_cell=[ T | F ]``
| If true, randomize the initial cell (currently by random walk)
| default: T
``LAMMPS_molecular_info=[ T | F ]``
| If true, create lammps bonds and other molecular MM features from initial atoms config (e.g. can be read from a file)
| default: T
``initial_walk_N_walks=int``
| number of initial random walks to apply to all walkers
| default: ''
``initial_walk_adjust_interval=int``
| in initial walks, interval between walks for adjusting step size
| default: ''
``initial_walk_Emax_offset_per_atom=float``
| offset (per atom) to increase Emax during initial random walk applied to all walkers
| default: 1.0
``initial_walk_only=[T | F]``
| do initial walk only, then quit
| default: F
``traj_interval=int``
| Iteration interval at which the currently culled configuration(s) is/are printed to the trajectory output, in the set format. If it is set <=0, no trajectory files will be printed at all. Useful option for larger runs as the trajectory files can become huge.
| default: 100
``delta_random_seed=int``
| Random number seed to be used in the run. If smaller than 0, a seed from /dev/urandom is used.
| default: -1
``no_extra_walks_at_all=[ T | F ]``
| default: F
``track_configs=[ T | F ]``
| Track configrations across all walks/clones.
| default: F
``track_configs_write=[ T | F ]``
| Write tracked configrations to a file (if false, tracking info will still be in snapshot and traj files)
| default: F
``ns_run_analyzers=string``
| Analyzers to apply during run. String consists of semicolon separated pairs of module name and intervals. Module names correspond to analysis modules in NS_PATH/ns_run_analyzers (see there for examples) or elsewhere in PYTHONPATH
| Positive interval refers to NS loop, negative to initial walks
| default:''
"""
sys.stderr.write("Usage: %s [ -no_mpi ] < input\n" % sys.argv[0])
sys.stderr.write("input:\n")
sys.stderr.write("max_volume_per_atom=float (1e3)\n")
sys.stderr.write("min_volume_per_atom=float (1)\n")
sys.stderr.write("start_species=int int [ float ] [, int int [ float ] ... ] (MANDATORY, this or start_config_file required. atomic_number multiplicity [not recomended: mass (amu)]. Info repeated for each species, separated by commas, mass is optional and not recommended.\n")
sys.stderr.write("start_config_file=str (MANDATORY, this or start_species required. if set filename to read initial atom information from (instead of creating them)\n")
sys.stderr.write("restart_file=path_to_file (file for restart configs. Mutually exclusive with start_*, one is required)\n")
sys.stderr.write("n_walkers=int (MANDATORY)\n")
sys.stderr.write("n_cull=int (1, number of walkers to kill at each NS iteration)\n")
sys.stderr.write("n_extra_walk_per_task=int (0)\n")
sys.stderr.write("n_iter_times_fraction_killed=int (MANDATORY, this or converge_down_to_T required)\n")
sys.stderr.write("converge_down_to_T=float (MANDATORY, this or n_iter_times_fraction_killed required)\n")
sys.stderr.write("T_estimate_finite_diff_lag=int (1000, lag for doing finite difference in current T estimate\n")
sys.stderr.write("min_Emax=float (None. Termination condition based on Emax)\n")
sys.stderr.write("out_file_prefix=str (None)\n")
sys.stderr.write("energy_calculator= ( ASE | lammps | internal | fortran) (fortran)\n")
sys.stderr.write("n_extra_data=int (0, amount of extra data per atom to pass around)\n")
sys.stderr.write("ns_run_analyzers=str ('', analyzers to apply during run\n")
sys.stderr.write("\n")
sys.stderr.write("KEmax_max_T=float (-1, if > 0 maximum temperature for estimating KEmax)\n")
sys.stderr.write("kB=float (8.6173324e-5, Boltzmann constant)\n")
sys.stderr.write("start_energy_ceiling_per_atom=float (1.0e9, max potential energy per atom for initial configs. P*Vmax is added to this automatically)\n")
sys.stderr.write("start_energy_ceiling=float (DEPRECATED: use start_energy_ceiling_per_atom. 1.0e9, max potential energy for initial configs. P*Vmax is added to this automatically)\n")
sys.stderr.write("random_init_max_n_tries=int (100, maximum number of tries for random initial positions under energy ceiling\n")
sys.stderr.write("\n")
sys.stderr.write("n_model_calls_expected=int (0, one of these is required)\n")
sys.stderr.write("n_model_calls=int (0, one of these is required)\n")
sys.stderr.write("do_good_load_balance=[T | F] (F, whether to do steps in groups with good load balance\n")
sys.stderr.write("\n")
sys.stderr.write("n_atom_steps=int (1, number of atomic trajectories \n")
sys.stderr.write("atom_traj_len=int (8, length of atomic trajectory (MD steps or MC sweeps) in each step\n")
sys.stderr.write("atom_traj_len_cost_multiplier=int (1, multiplier for cost of an atomic step (set to 1 for MD, TE-HMC, and SP-MC with O(1) cost moves, N for naive SP-MC)\n")
sys.stderr.write("break_up_atom_traj=[T | F] (F, whether to intersperse n_atom_steps atomic sub-trajectories with other types of steps\n")
sys.stderr.write("\n")
sys.stderr.write("n_cell_volume_steps=int (1, number of cell MC volume steps )\n")
sys.stderr.write("n_cell_shear_steps=int (1, number of cell MC shear steps )\n")
sys.stderr.write("n_cell_stretch_steps=int (1, number of cell MC stretch steps )\n")
sys.stderr.write("n_swap_steps=int (0, number of atom swaps)\n")
sys.stderr.write("swap_max_cluster=int (1, maximum size of interconnected cluster to try to swap)\n")
sys.stderr.write("swap_r_cut=float (2.5, cutoff radius for defining connected atoms for cluster)\n")
sys.stderr.write("swap_cluster_probability_increment=float (0.75, factor between prob. of picking increasing larger clusters)\n")
sys.stderr.write("swap_velo=[T | F] (F, if true, swap velocities when swapping atoms, breaking coherence a bit\n")
sys.stderr.write("swap_velo_fix_mag_alt=[T | F] (F, if true, use alternate correction for velocity magnitudes when not swapping velocities\n")
sys.stderr.write("\n")
sys.stderr.write("n_semi_grand_steps=int (0, number of species type changes)\n")
sys.stderr.write("semi_grand_potentials={ int : float [ , int : float ... ] } (None, chemical potentials for species type changes)\n")
sys.stderr.write("\n")
sys.stderr.write("velo_traj_len=int (0, number of MC sweeps in each velocity MC segement)\n")
sys.stderr.write("\n")
sys.stderr.write("random_energy_perturbation=float (1.0e-12)\n")
sys.stderr.write("atom_algorithm=[MC | MD | GMC] (MANDATORY)\n")
sys.stderr.write("\n")
sys.stderr.write("MC_atom_velocities=[T | F] (F, supported only for energy_calculator=fortran)\n")
sys.stderr.write("MC_atom_velocities_pre_perturb=[T | F] (F, Perturb velocities (rejection free) before MC + velocities walk)\n")
sys.stderr.write("MC_atom_step_size=float (1.0, in units of (max_volume_per_atom * N_atoms)^(1/3) )\n")
sys.stderr.write("MC_atom_step_size_max=float (1.0, in units of (max_volume_per_atom * N_atoms)^(1/3) )\n")
sys.stderr.write("MC_atom_uniform_rv=[T | F] (F)\n")
sys.stderr.write("MC_atom_Galilean=[T | F] (F)\n")
sys.stderr.write("GMC_no_reverse=[T | F] (T)\n")
sys.stderr.write("\n")
sys.stderr.write("MD_atom_velo_pre_perturb=[T | F] (F. Perturb velocities before MD trajectory\n")
sys.stderr.write("MD_atom_velo_post_perturb=[T | F] (T. Perturb velocities after MD trajectory\n")
sys.stderr.write("MD_atom_velo_flip_accept=[T | F] (F)\n")
sys.stderr.write("MD_atom_timestep=float (0.1, in ASE time units)\n")
sys.stderr.write("MD_atom_timestep_max=float (2.0, in ASE time units)\n")
sys.stderr.write("MD_atom_energy_fuzz=float (1.0e-2. Tolerance for rejecting non-energy conserving trajectories, as fraction of KE)\n")
sys.stderr.write("MD_atom_reject_energy_violation=[ T | F ] (F, use energy conservation violation (exceeding MD_atom_energy_fuzz * KE) to reject MD trajectories)\n")
sys.stderr.write("\n")
sys.stderr.write("atom_velo_rej_free_fully_randomize=[T | F] (F. If true, randomize velocities completely rather than just perturbing.\n")
sys.stderr.write("atom_velo_rej_free_perturb_angle=float (0.3. Max angle in radians for random rotations.)\n")
sys.stderr.write("MC_atom_velo_step_size=float (50.0)\n")
sys.stderr.write("MC_atom_velo_step_size_max=float (10000.0)\n")
sys.stderr.write("MC_atom_velo_walk_rej_free=[T | F] (T. If true, use rejection free algorithm for MC_atom_walk\n")
sys.stderr.write("\n")
sys.stderr.write("\n")
sys.stderr.write("MC_cell_P=float (0.0)\n")
sys.stderr.write("MC_cell_flat_V_prior=[T | F] (F). POORLY TESTED, DO NOT TRUST (YET)\n")
sys.stderr.write("MC_cell_volume_per_atom_step_size=float (5% of the maximum allowed volume)\n")
sys.stderr.write("MC_cell_volume_per_atom_step_size_max=float (50% of the maximum allowed volume)\n")
sys.stderr.write("MC_cell_volume_per_atom_prob=float (1.0)\n")
sys.stderr.write("MC_cell_stretch_step_size=float (0.1)\n")
sys.stderr.write("MC_cell_stretch_step_size_max=float (1.0)\n")
sys.stderr.write("MC_cell_stretch_prob=float (1.0)\n")
sys.stderr.write("MC_cell_shear_step_size=float (0.1 in units of (max_volume_per_atom * N_atoms)^(1/3))\n")
sys.stderr.write("MC_cell_shear_step_size_max=float (1.0 in units of (max_volume_per_atom * N_atoms)^(1/3))\n")
sys.stderr.write("MC_cell_shear_prob=float (1.0)\n")
sys.stderr.write("MC_cell_min_aspect_ratio=float (0.8)\n")
sys.stderr.write("cell_shape_equil_steps=int (1000)\n")
sys.stderr.write("\n")
sys.stderr.write("monitor_step_interval_times_fraction_killed=float (1, divided by n_cull/n_walkers to get actual monitoring interval in iterations, negative for only using last iteration, 0 for no monitoring)\n")
sys.stderr.write("adjust_step_interval_times_fraction_killed=float (5, divided by n_cull/n_walkers to get actual adjustment interval in iterations, negative for only using last iteration, 0 for no adjust)\n")
sys.stderr.write("full_auto_step_sizes=[T | F] (T) (T. Automatically calibrate all sizes by performing additional short explorations, including at start of run. F. Use initial input step sizes and make small adjustments to step sizes during run.)\n")
sys.stderr.write("MC_adjust_step_factor=float (1.1)\n")
sys.stderr.write("MC_adjust_min_rate=float (0.25)\n")
sys.stderr.write("MC_adjust_max_rate=float (0.75)\n")
sys.stderr.write("GMC_adjust_min_rate=float (0.25)\n")
sys.stderr.write("GMC_adjust_max_rate=float (0.75)\n")
sys.stderr.write("GMC_dir_perturb_angle=float (-1.0)\n")
sys.stderr.write("GMC_dir_perturb_angle_during=float (0.0)\n")
sys.stderr.write("MD_adjust_step_factor=float (1.1)\n")
sys.stderr.write("MD_adjust_min_rate=float (0.5)\n")
sys.stderr.write("MD_adjust_max_rate=float (0.95)\n")
sys.stderr.write("\n")
sys.stderr.write("ASE_calc_module=str (MANDATORY if energy_calculator=ASE)\n")
sys.stderr.write("FORTRAN_model=str (MANDATORY if energy_calculator=fortran)\n")
sys.stderr.write("FORTRAN_model_params=str (parameters for energy_calculator=fortran)\n")
sys.stderr.write("LAMMPS_fix_gmc=[T | F]\n")
sys.stderr.write("LAMMPS_init_cmds=str (MANDATORY if energy_calculator=lammps)\n")
sys.stderr.write("LAMMPS_name=str ('', arch name for lammps shared object file)\n")
sys.stderr.write("LAMMPS_serial=[T | F] (T, is lammps library serial)\n")
sys.stderr.write("LAMMPS_header=str (lammpslib.py value default, override lammpslib.py header commands for energy_calculator=lammps)\n")
sys.stderr.write("LAMMPS_header_extra=str ('', extra lammpslib.py header commands for energy_calculator=lammps)\n")
sys.stderr.write("LAMMPS_atom_types=symbol int [, symbol int ] ... ('', mapping from atomic symbols to type numbers for LAMMPS ASE interface)\n")
sys.stderr.write("\n")
sys.stderr.write("config_file_format=str (extxyz)\n") # julia
sys.stderr.write("rng=( numpy | internal | rngstream ) (numpy)\n") # julia
sys.stderr.write("profile=rank_to_profile (-1)\n")
sys.stderr.write("2D=[ T | F ] (F)\n")
sys.stderr.write("Z_cell_axis=float (10.0)\n")
sys.stderr.write("debug=debug_level (0, <= 0 for no debugging tests/prints)\n")
sys.stderr.write("snapshot_interval=int (10000, <=0 for no snapshots except final positions)\n")
sys.stderr.write("snapshot_time=int (3600, <=0 for no time based snapshots)\n")
sys.stderr.write("snapshot_per_parallel_task=[T | F] (T, if true save separate snapshot file for each parallel task)\n")
sys.stderr.write("snapshot_clean=[T | F] (T, if true clean previous iter snapshots\n")
sys.stderr.write("random_initialise_pos=[T | F] (T, if true randomize the initial positions\n")
sys.stderr.write("random_initialise_cell=[T | F] (T, if true randomize the initial cell\n")
sys.stderr.write("LAMMPS_molecular_info=[T | F] (T, if true create lammps molecular info\n")
sys.stderr.write("initial_walk_N_walks=int (0 number of rounds for initial walk) \n")
sys.stderr.write("initial_walk_adjust_interval=int (10 interval (in walks) between adjustments of steps during initial walk) \n")
sys.stderr.write("initial_walk_Emax_offset_per_atom=float (1.0, offset (per atom) to add to Emax for initial walks) \n")
sys.stderr.write("initial_walk_only=[T | F] (F, quit after doing initial walk) \n")
sys.stderr.write("traj_interval=int (100, <=0 for no trajectory)\n")
sys.stderr.write("delta_random_seed=int (-1, < 0 for seed from /dev/urandom)\n")
sys.stderr.write("no_extra_walks_at_all=[ T | F ] (F)\n")
sys.stderr.write("track_configs=[ T | F ] (F)\n")
sys.stderr.write("track_configs_write=[ T | F ] (F)\n")
def excepthook_mpi_abort(exctype, value, tb):
print( print_prefix,'Uncaught Exception Type:', exctype)
print( print_prefix,'Value:', value)
print( print_prefix,'Traceback:', tb)
# print_tb(tb)
print_exception(exctype, value, tb)
print( print_prefix, "Aborting")
sys.stdout.flush()
try:
MPI.COMM_WORLD.Abort(1)
except:
pass
sys.exit(1)
def exit_error(message, stat):
sys.stderr.write(message)
sys.stderr.flush()
try:
MPI.COMM_WORLD.Abort(stat)
except:
pass
sys.exit(stat)
def str_to_logical(x):
if x in [ "t", "T", "true", "True", "TRUE", "1" ]:
return True
if x in [ "f", "F", "false", "False", "FALSE", "0" ]:
return False
exit_error("Failed to parse logical\n",5)
def internal_Vlj(dr_vec, Eshift=0.0):
return (np.sum(dr_vec**-12 - dr_vec**-6) - Eshift*len(dr_vec))
# pos is Nx3, l is a scalar box size
def energy_internal_pos(pos, l):
n_at=np.size(pos,0)
dr_list = np.zeros( (n_at*(n_at-1)/2,3) )
offset=0
# calc relative pos vector
for i in range(n_at):
dr_list[offset:offset+(n_at-i-1),0] = pos[i,0] - pos[i+1:,0]
dr_list[offset:offset+(n_at-i-1),1] = pos[i,1] - pos[i+1:,1]
dr_list[offset:offset+(n_at-i-1),2] = pos[i,2] - pos[i+1:,2]
offset += n_at-i-1
# apply min image
dr_list[:,:] -= l*np.floor(dr_list[:,:]/l+0.5)
# magnitude of vectors
dr_list_mag = np.sqrt(np.sum(dr_list*dr_list,axis=1))
return np.sum(internal_Vlj(dr_list_mag[np.where(dr_list_mag < internal_cutoff)],Eshift))
def energy_internal(at):
return energy_internal_pos(at.get_positions(), at.get_cell()[0,0])
def eval_energy_PE(at):
if do_calc_ASE or do_calc_lammps:
if do_calc_lammps:
#NB only MD can make crazy positions, so maybe just do this after MD propagation?
at.wrap()
energy = at.get_potential_energy()
elif do_calc_internal:
energy = energy_internal(at)
elif do_calc_fortran:
energy = f_MC_MD.eval_energy(at)
else:
sys.stderr.write("No way to eval_energy_PE()\n", 5)
return energy
def eval_energy_KE(at):
if at.has('momenta') and at.has('masses'):
energy = at.get_kinetic_energy()
else:
energy = 0.0
return energy
def eval_energy_PV(at):
return movement_args['MC_cell_P']*at.get_volume()
def eval_energy_mu(at):
if movement_args['semi_grand_potentials'] is not None:
mu = movement_args['semi_grand_potentials']
energy = np.sum([ mu[Z] for Z in at.get_atomic_numbers() ])
else:
energy = 0.0
return energy
def eval_energy(at, do_KE=True, do_PE=True):
"""Calls appropriate functions to calculate the potential energy, kinetic energy and the p*V term.
"""
energy = eval_energy_PV(at) + eval_energy_mu(at)
if do_PE:
energy += eval_energy_PE(at)
if do_KE:
energy += eval_energy_KE(at)
return energy
def eval_forces(at):
if do_calc_ASE or do_calc_lammps:
if do_calc_lammps:
#NB only MD can make crazy positions, so maybe just do this after MD propagation?
at.wrap()
forces = at.get_forces()
elif do_calc_internal:
exit_error('no forces for do_calc_internal', 10)
elif do_calc_fortran:
forces = np.zeros( at.get_positions().shape )
f_MC_MD.eval_forces(at, forces)
else:
sys.stderr.write("No way to eval_forces()\n", 5)
return forces
def propagate_NVE_ASE(at, dt, n_steps):
at.calc = pot
# dt is being converted from ASE units to fs
if ns_args['debug'] >= 10:
vv = VelocityVerlet(at, dt=dt, logfile='-')
else:
vv = VelocityVerlet(at, dt=dt)
vv.run(n_steps)
def propagate_lammps(at, dt, n_steps, algo, Emax=None):
if pot.first_propagate:
pot.first_propagate=False
else:
pot.lmp.command('unfix 1 ')
# set appropriate fix
if algo == 'NVE':
pot.lmp.command('fix 1 all nve')
elif algo == 'GMC':
# hard coded value needed for LAMMPS. Let's hope our RNG maximum is at least this large
pot.lmp.command('fix 1 all gmc {} {} '.format(rng.int_uniform(1,900000000),Emax))
else:
exit_error("propagate_lammps got algo '%s', neither NVE nor GMC\n"% algo)
# NB maybe not do this every time? Just _after_ MD, since that's the only way position can become crazy?
at.wrap()
# do propagation, recover from exception if needed
try:
if algo == 'NVE':
pot.propagate(at, properties=['energy','forces'],system_changes=['positions'], n_steps=n_steps, dt=dt)
else:
pot.propagate(at, properties=['energy','forces'],system_changes=['positions'], n_steps=n_steps, dt=dt, dt_not_real_time=True)
except Exception as err:
# clean up and return failure
if ns_args['debug'] >= 4:
print( "propagate_lammps got exception ", err)
pot.restart_lammps(at)
pot.first_propagate=True
return False
return True
def velo_rv_mag(n):
if movement_args['2D']:
#unit_rv[:,2] = 0.0 # commented out from here, as unit_rv is unknown to this function at this point
nDOF = 2.0
else:
nDOF = 3.0
# In 3D rv_mag should have prob distrib p(r) = r^(3N-1).
# Using transformation rule p(y) = p(x) |dx/dy|, with p(y) = y^{3N-1} and p(x) = 1,
# one gets dx/dy = y^{3N-1}
# x = y^{3N}
# y = x^{1/3N}
return rng.float_uniform(0.0,1.0)**(1.0/(nDOF*n))
def velo_unit_rv(n):
unit_rv = rng.normal( 1.0, (n, 3) )
unit_rv /= np.linalg.norm(unit_rv)
return unit_rv
def gen_random_velo(at, KEmax, unit_rv=None):
if unit_rv is None:
unit_rv = velo_unit_rv(len(at))
if movement_args['2D']:
unit_rv[:,2] = 0.0
rv_mag = velo_rv_mag(len(at))
# from Baldock thesis Eq. 11.10
# p^{**} = r \mathbf{S} \hat{\mathbf{r}}
# and 11.11
# S_{ij} = \delta_{ij} (2 m_i [ E_{lim} - U(q))])^{1/2}
# p_i = r (2 m_i)^{1/2} (E-U)^{1/2} \hat{r}_i
# v_i = p_i / m_i = r (2/m)^{1/2} (E-U)^{1/2} \hat{r}_i
masses = at.get_masses()
velocities = rv_mag * np.sqrt(2.0/np.array([masses,]*3).transpose()) * np.sqrt(KEmax) * unit_rv
return velocities
def pairwise(iterable):
a = iter(iterable)
return zip(a, a)
def rotate_dir_3N(vec, max_ang):
if max_ang <= 0.0:
return
# apply random rotations
indices = [ (int(i/3), i%3) for i in range(3*vec.shape[0]) ]
rng.shuffle_in_place(indices)
for ((ind_1_a,ind_1_c), (ind_2_a,ind_2_c)) in pairwise(indices):
ang = rng.float_uniform(-max_ang,max_ang)
c_ang = np.cos(ang)
s_ang = np.sin(ang)
v_1 = vec[ind_1_a,ind_1_c] * c_ang + vec[ind_2_a,ind_2_c] * s_ang
v_2 = -vec[ind_1_a,ind_1_c] * s_ang + vec[ind_2_a,ind_2_c] * c_ang
vec[ind_1_a,ind_1_c] = v_1
vec[ind_2_a,ind_2_c] = v_2
def rotate_dir_3N_2D(vec, max_ang):
if max_ang <= 0.0:
return
# apply random rotations
indices = [ (int(i/2), i%2) for i in range(2*vec.shape[0]) ]
rng.shuffle_in_place(indices)
for ((ind_1_a,ind_1_c), (ind_2_a,ind_2_c)) in pairwise(indices):
ang = rng.float_uniform(-max_ang,max_ang)
c_ang = np.cos(ang)
s_ang = np.sin(ang)
v_1 = vec[ind_1_a,ind_1_c] * c_ang + vec[ind_2_a,ind_2_c] * s_ang
v_2 = -vec[ind_1_a,ind_1_c] * s_ang + vec[ind_2_a,ind_2_c] * c_ang
vec[ind_1_a,ind_1_c] = v_1
vec[ind_2_a,ind_2_c] = v_2
def rej_free_perturb_velo(at, Emax, KEmax, rotate=True):
#DOC
#DOC ``rej_free_perturb_velo``
if not at.has('momenta') or not at.has('masses'):
return
KEmax_use = None
if Emax is not None:
initial_KE = eval_energy_KE(at)
KEmax_use = Emax - (at.info['ns_energy'] - initial_KE)
if KEmax > 0.0:
if Emax is None:
KEmax_use = KEmax
else:
KEmax_use = min(KEmax_use, KEmax)
if KEmax_use is None:
exit_error("rej_free_perturb_velo() called with Emax and KEmax both None\n", 9)
orig_KE = at.get_kinetic_energy( )
#DOC \item if atom\_velo\_rej\_free\_fully\_randomize, pick random velocities consistent with Emax
if movement_args['atom_velo_rej_free_fully_randomize']:
# randomize completely
at.set_velocities(gen_random_velo(at, KEmax_use))
#DOC \item else perturb velocities
else: # perturb
velo = at.get_velocities()
velo_mag = np.linalg.norm(velo)
#DOC \item if current velocity=0, can't rescale, so pick random velocities consistent with Emax
if velo_mag == 0.0:
at.set_velocities(gen_random_velo(at, KEmax_use))
#DOC \item else, pick new random magnitude consistent with Emax, random rotation of current direction with angle uniform in +/- atom\_velo\_rej\_free\_perturb\_angle
else:
# pick new random magnitude - count on dimensionality to make change small
# WARNING: check this for variable masses
sqrt_masses_2D = np.sqrt(at.get_masses().reshape( (len(at),1) ))
scaled_vel = gen_random_velo(at, KEmax_use, velo/velo_mag) * sqrt_masses_2D
if rotate:
if movement_args['2D']:
rotate_dir_3N_2D(scaled_vel, movement_args['atom_velo_rej_free_perturb_angle'])
else:
rotate_dir_3N(scaled_vel, movement_args['atom_velo_rej_free_perturb_angle'])
at.set_velocities(scaled_vel / sqrt_masses_2D)
new_KE = at.get_kinetic_energy()
# rej_free_perturb_velo expects at.info['ns_energy'] to be set correctly initially
at.info['ns_energy'] += new_KE-orig_KE
def do_MC_atom_velo_walk(at, movement_args, Emax, nD, KEmax):
#DOC
#DOC ``do\_MC\_atom\_velo\_walk``
#DOC \item Else if MC\_atom\_velo\_walk\_rej\_free
if movement_args['MC_atom_velo_walk_rej_free']:
#DOC \item call rej\_free\_perturb\_velo()
rej_free_perturb_velo(at, Emax, KEmax)
return {}
#DOC \item else do MC pertubation to velocities
n_steps = movement_args['velo_traj_len']
step_size = movement_args['MC_atom_velo_step_size']
n_try = n_steps*len(at)
initial_KE = eval_energy_KE(at)
KEmax_use = Emax - (at.info['ns_energy'] - initial_KE) # expecting ns_energy = KE + PE (+PV)
if KEmax > 0.0 and KEmax < KEmax_use:
KEmax_use = KEmax
if do_calc_fortran:
(n_try, n_accept, final_KE) = f_MC_MD.MC_atom_walk_velo(at, n_steps, step_size, nD, KEmax_use)
at.info['ns_energy'] += final_KE-initial_KE
else:
masses = at.get_masses()
velocities = at.get_velocities()
KE = initial_KE
n_accept = 0
for i_step in range(n_steps):
at_list = list(range(len(at)))
rng.shuffle_in_place(at_list)
d_vel = rng.normal(step_size, (len(at), 3))
for i_at in at_list:
d_KE = 0.5*masses[i_at]*(np.sum((velocities[i_at,:]+d_vel[i_at,:])**2) - np.sum(velocities[i_at,:]**2))
if KE + d_KE < KEmax_use:
velocities[i_at,:] += d_vel[i_at,:]
KE += d_KE
n_accept += 1
at.set_velocities(velocities)
at.info['ns_energy'] += KE-initial_KE
return {'MC_atom_velo' : (n_try, n_accept)}
def do_MD_atom_walk(at, movement_args, Emax, KEmax):
#DOC
#DOC ``do_MD_atom_walk``
""" perform MD walk on the configuration """
orig_E = at.info['ns_energy']
nD=3
if movement_args['2D']:
nD=2
if orig_E >= Emax:
print( print_prefix, ": WARNING: orig_E =",orig_E," >= Emax =",Emax)
#DOC \item if MD\_atom\_velo\_pre\_perturb, call do\_MC\_atom\_velo\_walk() for magnitude and rotation
if movement_args['MD_atom_velo_pre_perturb']:
do_MC_atom_velo_walk(at, movement_args, Emax, nD, KEmax)
pre_MD_pos = at.get_positions()
pre_MD_velo = at.get_velocities()
if ns_args['n_extra_data'] > 0:
pre_MD_extra_data = at.arrays['ns_extra_data'].copy()
pre_MD_E = at.info['ns_energy']
#DOC \item propagate in time atom\_traj\_len time steps of length MD\_atom\_timestep
if movement_args['python_MD']:
forces = eval_forces(at)
final_E = None
timestep = movement_args['MD_atom_timestep']
for i in range(movement_args['atom_traj_len']):
at.set_momenta(at.get_momenta()+forces*0.5*timestep)
at.set_positions(at.get_positions()+at.get_momenta()*timestep)
try:
forces = eval_forces(at)
except:
final_E = 2.0*abs(Emax)
break
at.set_momenta(at.get_momenta()+forces*0.5*timestep)
if final_E is None: # didn't abort due to exception in eval_forces()
final_E = eval_energy(at)
else:
if do_calc_ASE:
propagate_NVE_ASE(at, dt=movement_args['MD_atom_timestep'], n_steps=movement_args['atom_traj_len'])
final_E = eval_energy(at)
elif do_calc_lammps:
if propagate_lammps(at, dt=movement_args['MD_atom_timestep'], n_steps=movement_args['atom_traj_len'], algo='NVE'):
final_E = pot.results['energy'] + eval_energy(at, do_PE=False)
else: # propagate returned success == False
final_E = 2.0*abs(Emax)
## print("error in propagate_lammps NVE, setting final_E = 2*abs(Emax) =" , final_E)
elif do_calc_fortran:
final_E = f_MC_MD.MD_atom_NVE_walk(at, n_steps=movement_args['atom_traj_len'], timestep=movement_args['MD_atom_timestep'], debug=ns_args['debug'])
final_E += eval_energy(at, do_PE=False, do_KE=False)
else: