-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathconfig.rs
1444 lines (1259 loc) · 52.3 KB
/
config.rs
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
/* ************************************************************************ **
** This file is part of rsp2, and is licensed under EITHER the MIT license **
** or the Apache 2.0 license, at your option. **
** **
** http://www.apache.org/licenses/LICENSE-2.0 **
** http://opensource.org/licenses/MIT **
** **
** Be aware that not all of rsp2 is provided under this permissive license, **
** and that the project as a whole is licensed under the GPL 3.0. **
** ************************************************************************ */
// NOTE: Please make sure to use the YamlRead trait when deserializing these types!
//
// DO NOT USE serde_yaml::from_{reader,value,etc.} OUTSIDE THIS CRATE
// or else you defeat the entire reason for YamlRead's existence.
// (NOTE: I can't enforce this through the type system without completely destroying
// the ergonomics of these types. Just Ctrl+Shift+F the workspace for "serde_yaml"
// if compile times seem suspiciously off...)
use serde::de::{self, IntoDeserializer};
pub const MAX_VERSION: u32 = 1;
use std::collections::HashMap;
use std::fmt;
use crate::option_aliases::{OrDefault, Nullable, Filled};
/// Root settings object.
///
/// This is what you should deserialize.
#[derive(Serialize)]
#[derive(Debug, Clone, PartialEq)]
pub struct ValidatedSettings(pub Settings);
/// Raw deserialized form of settings.
///
/// You shouldn't deserialize this type directly; deserialize `ValidatedSettings` instead,
/// so that additional validation and filling of defaults can be performed.
/// (e.g. incompatible settings, or options whose defaults depend on others)
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct Settings {
/// Identifies the version of the settings that this file uses.
///
/// rsp2 increments the max supported version number when breaking changes are made to
/// config files. Old config files with an older version continue to use the old behavior.
///
/// If not specified, assumes a value of 1.
#[serde(default)]
pub version: OrDefault<u32>,
#[serde(default)]
pub threading: Threading,
/// Specifies the potential to be used.
///
/// See [`PotentialKind`] for the list of possibilities.
pub potential: ValidatedPotential,
/// Specifies parameters for bond polarizability model.
#[serde(default)]
pub bond_polarizability: rsp2_bond_polarizability::Settings,
// (FIXME: weird name)
/// Used to optimize lattice parameters prior to relaxation.
///
/// See the type for documentation.
#[serde(default)]
pub scale_ranges: ScaleRanges,
/// Names of parameters, using the same letter for parameters that should scale together.
///
/// This is used to enable optimization of lattice vector lengths during CG,
/// if the potential supports it. (optimization of cell angles is not supported)
///
/// Note that the same letter does **not** mean that the lengths must be *equal;*
/// it simply means that their ratio of lengths will be preserved.
/// Use `null` (or equivalently `~`) for parameters that should not be scaled.
/// (e.g. vacuum separation)
///
/// # Example:
///
/// ```yaml
/// # e.g. graphite
/// parameters: [a, a, c]
///
/// # e.g. chain along z
/// parameters: [~, ~, c]
/// ```
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Nullable<Parameters>,
/// Provide relaxation of the top left 2x2 lattice submatrix.
///
/// If supplied (an empty mapping will do), `parameters` will be ignored and
/// all four elements of the 2x2 submatrix will be optimized.
///
/// This is a temporary hack until a more natural design for how to write
/// config for this can be designed.
///
/// # Example:
///
/// ```yaml
/// lattice-relax-22: {}
/// ```
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub lattice_relax_22: Nullable<LatticeRelax>,
/// See the type for documentation.
#[serde(default)]
pub acoustic_search: AcousticSearch,
/// See the type for documentation.
pub cg: Cg,
/// See the type for documentation.
pub phonons: Nullable<Phonons>,
/// See the type for documentation.
pub ev_chase: EigenvectorChase,
/// `None` disables layer search.
/// (layer_search is also ignored if layers.yaml is provided)
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub layer_search: Nullable<LayerSearch>,
// FIXME: I'm beginning to think there shouldn't be a global bond graph.
// Potentials should manage their own bond graphs.
//
/// `None` disables bond graph.
///
/// Sometimes this is ignored if a bond graph needs to be generated on the spot.
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub bond_radius: Nullable<f64>,
// FIXME move
pub layer_gamma_threshold: f64,
/// See the type for documentation.
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub masses: Nullable<Masses>,
/// See the type for documentation.
#[serde(default)]
pub ev_loop: EvLoop,
/// `None` disables band unfolding.
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub unfold_bands: Option<UnfoldBands>,
/// `None` disables animations.
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub animate: Option<Animate>,
/// See the type for documentation.
#[serde(default)]
pub snapshot: Snapshot,
#[serde(default)]
#[serde(flatten)]
pub _deprecated_lammps_settings: DeprecatedLammpsSettings,
/// See the type for documentation.
#[serde(default)]
pub lammps: Lammps,
}
derive_yaml_read!{ValidatedSettings}
impl<'de> de::Deserialize<'de> for ValidatedSettings {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let cereal: Settings = de::Deserialize::deserialize(deserializer)?;
cereal.validate().map_err(de::Error::custom)
}
}
// intended to be `#[serde(flatten)]`-ed into other types
#[derive(Default)]
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct DeprecatedLammpsSettings {
#[serde(default)]
#[serde(rename = "lammps-update-style")]
#[serde(skip_serializing_if = "Option::is_none")]
pub lammps_update_style: Option<LammpsUpdateStyle>,
#[serde(default)]
#[serde(rename = "lammps-processor-axis-mask")]
#[serde(skip_serializing_if = "Option::is_none")]
pub lammps_processor_axis_mask: Option<[bool; 3]>,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct ScaleRanges {
/// TODO: Document
pub scalables: Vec<Scalable>,
/// How many times to repeat the process of relaxing all parameters.
///
/// This may yield better results if one of the parameters relaxed
/// earlier in the sequence impacts one of the ones relaxed earlier.
#[serde(default="scale_ranges__repeat_count")]
pub repeat_count: u32,
/// Warn if the optimized value of a parameter falls within this amount of
/// the edge of the search window (relative to the search window size),
/// which likely indicates that the search window was not big enough.
///
/// If null (`~`), no check is performed.
#[serde(default="scale_ranges__warn_threshold")]
#[serde(skip_serializing_if = "Option::is_none")]
pub warn_threshold: Nullable<f64>,
/// Panic on violations of `warn_threshold`.
#[serde(default="scale_ranges__fail")]
pub fail: bool,
}
fn scale_ranges__repeat_count() -> u32 { 1 }
fn scale_ranges__warn_threshold() -> Nullable<f64> { Some(0.01) }
fn scale_ranges__fail() -> bool { false }
// Require "scalables" if "scale-ranges" is provided, but allow it to be defaulted to
// an empty list otherwise.
impl Default for ScaleRanges {
fn default() -> Self {
ScaleRanges {
scalables: vec![],
repeat_count: scale_ranges__repeat_count(),
warn_threshold: scale_ranges__warn_threshold(),
fail: scale_ranges__fail(),
}
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct LatticeRelax {}
pub type Parameters = [Parameter; 3];
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Parameter {
Param(char),
One,
NotPeriodic,
}
impl<'de> serde::Deserialize<'de> for Parameter {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de>,
{
use serde::de::Unexpected;
use serde::de::Error;
struct Visitor;
impl<'a> serde::de::Visitor<'a> for Visitor {
type Value = Parameter;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "1, a single character, or null")
}
fn visit_i64<E>(self, value: i64) -> Result<Parameter, E>
where E: Error,
{ match value {
1 => Ok(Parameter::One),
n => Err(Error::invalid_value(Unexpected::Signed(n), &self)),
}}
fn visit_str<E>(self, s: &str) -> Result<Parameter, E>
where E: Error,
{ match s.len() {
1 => Ok(Parameter::Param(s.chars().next().unwrap())),
_ => Err(Error::invalid_value(Unexpected::Str(s), &self)),
}}
fn visit_unit<E>(self) -> Result<Parameter, E>
where E: Error,
{ Ok(Parameter::NotPeriodic) }
}
deserializer.deserialize_any(Visitor)
}
}
impl serde::Serialize for Parameter {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer,
{ match *self {
Parameter::One => serializer.serialize_i32(1),
Parameter::NotPeriodic => serializer.serialize_none(),
Parameter::Param(c) => serializer.serialize_char(c),
}}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Scalable {
/// Uniformly scale one or more lattice vectors.
#[serde(rename = "parameter")]
#[serde(rename_all = "kebab-case")]
Param {
axis_mask: [MaskBit; 3],
#[serde(flatten)]
range: ScalableRange,
},
/// Optimize a single value shared by multiple layer separations.
///
/// Under certain conditions, the optimum separation IS identical for
/// all layers (e.g. generated structures where all pairs of layers
/// look similar, and where the potential only affects adjacent layers).
///
/// There are also conditions where the separation obtained from this method
/// is "good enough" that CG can be trusted to take care of the rest.
#[serde(rename_all = "kebab-case")]
UniformLayerSep {
/// Toggle which separations are affected. For n layers, this must have n-1 elements.
#[serde(default)]
mask: OrDefault<Vec<MaskBit>>,
#[serde(flatten)]
range: ScalableRange,
},
/// Optimize each layer separation individually. Can be costly.
#[serde(rename_all = "kebab-case")]
LayerSeps {
/// Toggle which separations are affected. For n layers, this must have n-1 elements.
#[serde(default)]
mask: OrDefault<Vec<MaskBit>>,
#[serde(flatten)]
range: ScalableRange,
},
}
// a bool that serializes as an integer
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct MaskBit(pub bool);
impl serde::Serialize for MaskBit {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer,
{ (self.0 as i32).serialize(serializer) }
}
impl<'de> serde::Deserialize<'de> for MaskBit {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de>,
{
use serde::de::Unexpected;
use serde::de::Error;
match serde::Deserialize::deserialize(deserializer)? {
0i64 => Ok(MaskBit(false)),
1i64 => Ok(MaskBit(true)),
n => Err(Error::invalid_value(Unexpected::Signed(n), &"a mask bit equal to 0 or 1")),
}
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum ScalableRange {
// NOTE: This enum gets `serde(flatten)`ed into its container. Beware field-name clashes.
#[serde(rename_all = "kebab-case")]
Search {
range: (f64, f64),
/// A "reasonable value" that might be used while another
/// parameter is optimized.
#[serde(default)]
guess: OrDefault<f64>,
},
#[serde(rename_all = "kebab-case")]
Exact {
value: f64,
},
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all="kebab-case")]
pub enum UnfoldBands {
/// Use a method based on Zheng, Fawei; Zhang, Ping (2017).
/// "Phonon Unfolding: A program for unfolding phonon dispersions of materials",
/// Mendeley Data, v1 <http://dx.doi.org/10.17632/3hpx6zmxhg.1>
///
/// This has not been used much lately, and I lack confidence in the correctness of its
/// implementation. My honest suggestion is: don't bother.
///
/// Allen's method (2013) is vastly superior, but is not currently integrated into the rsp2
/// binaries. See the standalone script `scripts/unfold.py` in the rsp2 source root.
Zheng {}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all="kebab-case")]
pub struct Cg {
/// CG stop conditions use a little DSL.
///
/// `any` and `all` can be arbitrarily nested.
#[serde()]
pub stop_condition: CgStopCondition,
#[serde(default)]
pub flavor: CgFlavor,
#[serde(default)]
pub on_ls_failure: CgOnLsFailure,
/// Clip initial guesses for linesearch at this value each iteration.
#[serde(default = "cg__alpha_guess_first")]
pub alpha_guess_first: f64,
/// Initial guess for linesearch on the very first iteration.
#[serde(default = "cg__alpha_guess_max")]
pub alpha_guess_max: f64,
}
// Been using these values for a while on structures of arbitrary size.
fn cg__alpha_guess_first() -> f64 { 0.01 }
fn cg__alpha_guess_max() -> f64 { 0.1 }
pub type CgStopCondition = rsp2_minimize::cg::StopCondition;
/// Behavior when a linesearch along the steepest descent direction fails.
/// (this is phenomenally rare for the Hager linesearch method, and when it
/// does occur it may very well be due to exceptionally good convergence,
/// rather than any sort of actual failure)
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all="kebab-case")]
pub enum CgOnLsFailure {
/// Treat a second linesearch failure as a successful stop condition.
Succeed,
/// Succeed, but log a warning.
Warn,
/// Complain loudly and exit with a nonzero exit code.
Fail,
}
impl Default for CgOnLsFailure {
fn default() -> Self { CgOnLsFailure::Succeed }
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all="kebab-case")]
pub enum CgFlavor {
#[serde(rename_all="kebab-case")]
Acgsd {
#[serde(rename="iteration-limit")] // for compatibility
ls_iteration_limit: OrDefault<u32>,
},
#[serde(rename_all="kebab-case")]
Hager {},
}
impl Default for CgFlavor {
fn default() -> Self { CgFlavor::Hager {} }
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct LayerSearch {
/// Miller index of the family of planes. Must have `gcd == 1`.
#[serde(alias = "miller")]
pub normal: [i32; 3],
/// The cutoff distance that decides whether two atoms belong to the same layer;
/// if and only if the shortest distance between them (projected onto the normal)
/// exceeds this value, they belong to separate layers.
pub threshold: f64,
/// Expected number of layers, for a sanity check.
/// (rsp2 will fail if this is provided and does not match the count found)
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub count: Nullable<u32>,
}
derive_yaml_read!{LayerSearch}
#[derive(Serialize)]
#[derive(Debug, Clone, PartialEq)]
pub struct ValidatedEnergyPlotSettings(pub EnergyPlotSettings);
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct EnergyPlotSettings {
#[serde(default)]
pub version: OrDefault<u32>,
#[serde(default)]
pub threading: Threading,
pub potential: ValidatedPotential,
#[serde(default)]
#[serde(flatten)]
pub _deprecated_lammps_settings: DeprecatedLammpsSettings,
#[serde(default)]
pub lammps: Lammps,
}
derive_yaml_read!{ValidatedEnergyPlotSettings}
impl<'de> de::Deserialize<'de> for ValidatedEnergyPlotSettings {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let cereal: EnergyPlotSettings = de::Deserialize::deserialize(deserializer)?;
cereal.validate().map_err(de::Error::custom)
}
}
/// Potential settings known to satisfy certain properties:
///
/// * No deprecated items.
/// * Is known to have at most one `lammps` entry.
/// * Is known to have at most one `dftb+` entry.
#[derive(Serialize)]
#[derive(Debug, Clone, PartialEq)]
pub struct ValidatedPotential(pub Potential);
impl<'de> de::Deserialize<'de> for ValidatedPotential {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let cereal: Potential = de::Deserialize::deserialize(deserializer)?;
cereal.validate().map_err(de::Error::custom)
}
}
derive_yaml_read!{ValidatedPotential}
#[derive(Serialize)]
#[derive(Debug, Clone, PartialEq)]
pub struct Potential(pub Vec<PotentialKind>);
impl Potential {
pub fn into_vec(self) -> Vec<PotentialKind> { self.0 }
pub fn as_slice(&self) -> &[PotentialKind] { &self.0 }
}
// Manual impl, because #[derive(Deserialize)] on untagged enums discard
// all error messages.
impl<'de> de::Deserialize<'de> for Potential {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct MyVisitor;
impl<'de> de::Visitor<'de> for MyVisitor {
type Value = Potential;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a potential or array of potentials")
}
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut vec = vec![];
while let Some(pot) = seq.next_element()? {
vec.push(pot);
}
Ok(Potential(vec))
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
de::Deserialize::deserialize(s.into_deserializer())
.map(|x| Potential(vec![x]))
}
fn visit_map<A: de::MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
de::Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))
.map(|x| Potential(vec![x]))
}
}
deserializer.deserialize_any(MyVisitor)
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
pub enum PotentialKind {
// Deprecated names. If you use these, a warning will be printed telling you the new proper
// thing to write in your config.
#[serde(rename = "rebo")] OldLammpsRebo(LammpsPotentialRebo),
#[serde(rename = "airebo")] OldLammpsAirebo(LammpsPotentialAirebo),
#[serde(rename = "kc-z")] OldLammpsKolmogorovCrespiZ(LammpsPotentialKolmogorovCrespiZ),
#[serde(rename = "kc-full")] OldLammpsKolmogorovCrespiFull(LammpsPotentialKolmogorovCrespiFull),
#[serde(rename = "kc-z-new")] OldKolmogorovCrespiZ(OldPotentialKolmogorovCrespiZ),
#[serde(rename = "rebo-new")] OldReboNew(PotentialReboNonreactive),
/// Reimplementation of LAMMPS' `kolmogorov/crespi/z` and/or `kolmogorov/crespi/full`.
///
/// This potential only contains an interlayer term, and is typically summed together with
/// `rebo-nonreactive`.
///
/// This is implemented directly in rsp2 and supports lattice parameter optimization.
/// It can also be optionally given a smooth cutoff with C(1) continuity.
///
/// Even though arbitrary normals are supported, the implementation is still only fit for
/// materials that are layered along the z-axis. This is because the implementation is
/// historically based off of `kolmogorov/crespi/z`, and thus is designed to suppress
/// interactions between non-adjacent layers. (e.g. between layers 1 and 3 in a trilayer).
#[serde(rename = "kc-layered")] KolmogorovCrespi(PotentialKolmogorovCrespi),
/// REBO, without fractional bond orders.
///
/// This is implemented directly in rsp2 and supports lattice parameter optimization.
/// It also supports a variety of parameters.
#[serde(rename = "rebo-nonreactive")] ReboNonreactive(PotentialReboNonreactive),
/// Use potentials implemented in Lammps. Only a few specific potentials are supported.
///
/// This potential cannot be listed multiple times.
#[serde(rename = "lammps")] Lammps(LammpsPotentialKind),
/// Use arbitrary potentials implemented in DFTB+.
///
/// This potential cannot be listed multiple times.
#[serde(rename = "dftb+")] DftbPlus(PotentialDftbPlus),
/// Wyatt Gibbon's product-of-cosines external potential for hexagonal systems.
#[serde(rename = "gibbons-product")] GibbonsProduct(PotentialGibbonsProduct),
/// V = 0
#[serde(rename = "test-func-zero")] TestZero,
/// Arranges atoms into a chain along the first lattice vector.
#[serde(rename = "test-func-chainify")] TestChainify,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum LammpsPotentialKind {
/// `pair_style rebo`
#[serde(rename = "rebo")] Rebo(LammpsPotentialRebo),
/// `pair_style airebo`
#[serde(rename = "airebo")] Airebo(LammpsPotentialAirebo),
/// `pair_style hybrid rebo kolmogorov/crespi/z`
///
/// Notice how, unlike the reimplementation of `kc-z`, this potential automatically includes REBO.
#[serde(rename = "kc-z")] KolmogorovCrespiZ(LammpsPotentialKolmogorovCrespiZ),
/// `pair_style hybrid rebo kolmogorov/crespi/full`
#[serde(rename = "kc-full")] KolmogorovCrespiFull(LammpsPotentialKolmogorovCrespiFull),
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct LammpsPotentialAirebo {
// NOTE: some defaults are not here because they are defined in rsp2_tasks,
// which depends on this crate
/// Cutoff radius (x3.4A)
#[serde(skip_serializing_if = "Option::is_none")]
pub lj_sigma: OrDefault<f64>,
// (I'm too lazy to make an ADT for this)
#[serde(skip_serializing_if = "Option::is_none")]
pub lj_enabled: OrDefault<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub torsion_enabled: OrDefault<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub omp: OrDefault<bool>,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct LammpsPotentialRebo {
#[serde(skip_serializing_if = "Option::is_none")]
pub omp: OrDefault<bool>,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct LammpsPotentialKolmogorovCrespiZ {
#[serde(default = "potential__lammps_kolmogorov_crespi_z__rebo")]
#[serde(skip_serializing_if = "potential__lammps_kolmogorov_crespi_z__rebo__skip")]
pub rebo: bool,
/// Cutoff radius (Angstrom?)
#[serde(skip_serializing_if = "Option::is_none")]
pub cutoff: OrDefault<f64>,
/// Separations larger than this are regarded as vacuum and do not interact. (Angstrom)
#[serde(skip_serializing_if = "Option::is_none")]
pub max_layer_sep: OrDefault<f64>,
/// Enable a smooth cutoff starting at `r = cutoff - cutoff_interval` and ending at
/// `r = cutoff`.
///
/// NOTE: This requires a patched lammps.
#[serde(skip_serializing_if = "Option::is_none")]
pub cutoff_interval: Nullable<f64>,
}
fn potential__lammps_kolmogorov_crespi_z__rebo() -> bool { true }
fn potential__lammps_kolmogorov_crespi_z__rebo__skip(&x: &bool) -> bool { x == true }
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct LammpsPotentialKolmogorovCrespiFull {
#[serde(default = "potential__lammps_kolmogorov_crespi_full__rebo")]
#[serde(skip_serializing_if = "potential__lammps_kolmogorov_crespi_full__rebo__skip")]
pub rebo: bool,
/// Cutoff radius (Angstrom?)
#[serde(skip_serializing_if = "Option::is_none")]
pub cutoff: OrDefault<f64>,
/// Parameterization.
#[serde(default = "potential__lammps_kolmogorov_crespi_full__params")]
pub params: LammpsKolmogorovCrespiParams,
}
fn potential__lammps_kolmogorov_crespi_full__params() -> LammpsKolmogorovCrespiParams {
LammpsKolmogorovCrespiParams::Ouyang { taper: false }
}
fn potential__lammps_kolmogorov_crespi_full__rebo() -> bool { true }
fn potential__lammps_kolmogorov_crespi_full__rebo__skip(&x: &bool) -> bool { x == true }
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum LammpsKolmogorovCrespiParams {
/// Uses the file `CC.KC-full` and sets `taper = 0`.
///
/// # Citation
/// A.N. Kolmogorov & V. H. Crespi,
/// Registry-dependent interlayer potential for graphitic systems.
/// Physical Review B 71, 235415 (2005)
Original,
/// Uses the file `CH.KC` or `CH_taper.KC`, and sets the `taper` flag accordingly.
///
/// # Citation
/// Wengen Ouyang, Davide Mandelli, Michael Urbakh, Oded Hod, arXiv:1806.09555 (2018).
Ouyang { taper: bool },
}
impl LammpsKolmogorovCrespiParams {
pub fn taper(&self) -> bool {
match *self {
LammpsKolmogorovCrespiParams::Original => false,
LammpsKolmogorovCrespiParams::Ouyang { taper } => taper,
}
}
pub fn filename(&self) -> &str {
match *self {
LammpsKolmogorovCrespiParams::Original => "CC.KC-full",
LammpsKolmogorovCrespiParams::Ouyang { taper: false } => "CH.KC",
LammpsKolmogorovCrespiParams::Ouyang { taper: true } => "CH_taper.KC",
}
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct OldPotentialKolmogorovCrespiZ {
#[serde(rename = "cutoff")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cutoff_begin: OrDefault<f64>,
#[serde(rename = "cutoff-length")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cutoff_transition_dist: OrDefault<f64>,
#[serde(default = "potential_kolmogorov_crespi_new__skin_depth")]
pub skin_depth: f64,
#[serde(default = "potential_kolmogorov_crespi_new__skin_check_frequency")]
#[serde(skip_serializing_if = "potential_kolmogorov_crespi_new__skin_check_frequency__skip")]
pub skin_check_frequency: u64,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct PotentialKolmogorovCrespi {
// NOTE: defaults are not here because they are defined in rsp2_tasks,
// which depends on this crate
/// Cutoff radius. (Angstrom)
///
/// More specifically, it is the maximum radius where the cutoff prefactor has a value of 1.
#[serde(rename = "cutoff")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cutoff_begin: OrDefault<f64>,
pub params: KolmogorovCrespiParams,
/// Thickness of the "smooth cutoff" shell. (Angstrom)
///
/// NOTE: If a value of 0.0 is used, the value is offset to maintain C0 continuity.
/// (This makes it effectively identical to LAMMPS)
#[serde(rename = "cutoff-length")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cutoff_transition_dist: OrDefault<f64>,
/// Skin depth for neighbor searches. (Angstrom)
///
/// Adjusting this may wildly improve (or hurt!) performance depending on the application.
///
/// This should be larger than the phonon displacement distance, or else phonon forces
/// may be incorrect.
#[serde(default = "potential_kolmogorov_crespi_new__skin_depth")]
pub skin_depth: f64,
// FIXME: hack
/// Perform a skin check every `n` computations (`0` = never) rather than every computation.
///
/// Even though it may give slightly incorrect results, this is provided because in some cases,
/// states speculatively observed by an algorithm (such as conjugate gradient with built-in
/// param optimization) may have a large tendency to briefly violate the skin check. For such
/// purposes it is expected that the (stronger) forces from REBO will quickly discourage CG from
/// actually selecting states with drastically modified neighbor lists, and that the initial
/// bond list should remain sufficient for all states actually visited by CG.
///
/// Because various parts of the code may call the potential any arbitrary number of times,
/// the frequency here does not necessarily correspond to anything meaningful.
#[serde(default = "potential_kolmogorov_crespi_new__skin_check_frequency")]
#[serde(skip_serializing_if = "potential_kolmogorov_crespi_new__skin_check_frequency__skip")]
pub skin_check_frequency: u64,
pub normals: KolmogorovCrespiNormals,
}
fn potential_kolmogorov_crespi_new__skin_depth() -> f64 { 1.0 }
fn potential_kolmogorov_crespi_new__skin_check_frequency() -> u64 { 1 }
fn potential_kolmogorov_crespi_new__skin_check_frequency__skip(&x: &u64) -> bool { x == 1 }
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum KolmogorovCrespiNormals {
/// Assume all normals are z-oriented.
Z { },
/// Use local normals, as originally implemented in rsp2.
///
/// These are the normals to the unique plane containing the three neighbors around each atom.
/// If any sites do not have three neighbors, this will fail.
///
/// This differs slightly from the original definition in the paper, since this definition does
/// not depend on the position of the current atom. For backwards compatibility, `local` still
/// maps to this.
///
/// This definition is certainly simpler and involves fewer floating point operations, hence
/// the name "fast." Don't know *how* much faster it actually is, though.
#[serde(alias = "local")] // back-compat
LocalFast { },
/// Use local normals, as *actually* defined in Kolmogorov and Crespi, 2005.
///
/// The exact definition comes from this sentence in the paper:
/// "For example, one can average the three normalized cross
/// products of the displacement vectors to the nearest neighbors."
///
/// Kolmogorov and Crespi report that, for nanotubes, the normals generated by this method tend
/// to tilt towards the center of one of the hexagonal faces, making this definition suboptimal.
LocalTrue { },
// semilocal is not implemented
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum KolmogorovCrespiParams {
/// Parameters lifted from LAMMPS' `CC.KC-full`.
///
/// # Citation
/// A.N. Kolmogorov & V. H. Crespi,
/// Registry-dependent interlayer potential for graphitic systems.
/// Physical Review B 71, 235415 (2005)
Original,
/// Parameters lifted from LAMMPS' `CH.KC`.
///
/// # Citation
/// Wengen Ouyang, Davide Mandelli, Michael Urbakh, Oded Hod, arXiv:1806.09555 (2018).
Ouyang,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct PotentialReboNonreactive {
/// "brenner" or "lammps"
pub params: PotentialReboNewParams,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[derive(Copy)]
#[serde(rename_all = "kebab-case")]
pub enum PotentialReboNewParams {
Brenner,
Lammps,
LammpsFavata,
/// Experimental feature.
///
/// This currently yields absurdly large frequencies for
/// Bernal-stacked BLG and may require further investigation.
#[serde(rename = "experimental-lindsay")]
Lindsay,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct PotentialDftbPlus {
/// An HSD file embedded as a multiline string.
///
/// Please omit `Geometry` and `Driver`.
///
/// Also, notice that rsp2 will always supply `Periodic = Yes`, even for isolated
/// molecules. For these, you will want to supply `KPointsAndWeights { 0.0 0.0 0.0 1.0 }`
/// in the `Hamiltonian` section.
///
/// Be aware that when `dftb+` is run, it will be run in a temporary directory,
/// breaking all relative paths in the document. For this reason, you must use
/// absolute paths.
pub hsd: String,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct PotentialGibbonsProduct {
/// The number of maxima in the potential along each direction. Must be `>= 1`.
#[serde(default = "potential_gibbons_product__num_maxima")]
pub num_maxima: u32,
/// The amplitude of the potential.
pub amplitude: f64,
}
fn potential_gibbons_product__num_maxima() -> u32 { 1 }
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum EigenvectorChase {
OneByOne,
Cg(Cg),
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct Phonons {
/// Cartesian distance threshold for determining if two sites are equivalent under
/// a symmetry operator.
///
/// A value for this is **required** in most use cases. (the exception is when
/// `analytic-hessian: true`)
///
/// If a value of 0 is used, symmetry will not be sought. This is necessary sometimes
/// to work around limitations that prevent rsp2 from working on non-primitive cells.
pub symmetry_tolerance: Nullable<f64>,
/// How far atoms are displaced when numerically computing the force constants.
///
/// A value for this is **required** in most use cases. (the exception is when
/// `analytic-hessian: true`)
pub displacement_distance: Nullable<f64>,
/// Use an analytically-computed hessian for the force constants.
///
/// If true, `symmetry_tolerance`, and `displacement_distance` are allowed to be null.
/// Currently, symmetry will not be imposed in any form when this is true.
///
/// The vast majority of potentials do NOT support this. (in fact, no complete potential
/// currently supports this; only the rust implementation of KCZ's interlayer potential,
/// without the contributions from REBO)
#[serde(default = "phonons__analytic_hessian")]
pub analytic_hessian: bool,
/// How displacements are generated.
///
/// A value for this is **required** in most use cases. (the exception is when
/// `analytic-hessian: true`)
#[serde(default = "phonons__disp_finder")]
pub disp_finder: PhononDispFinder,
#[serde(default = "phonons__eigensolver")]
pub eigensolver: PhononEigensolver,
/// Method of imposing an acoustic sum rule on the force constants.
///