1use std::collections::HashMap;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum KeywordCategory {
15 Required,
17 Optional,
19 Convergence,
21 Program,
23 TdDft,
25 Constraints,
27 Advanced,
29}
30
31#[derive(Debug, Clone)]
33pub struct Keyword {
39 pub name: &'static str,
41 pub category: KeywordCategory,
43 pub description: &'static str,
45 pub default_value: Option<&'static str>,
47 pub example: Option<&'static str>,
49 pub required: bool,
51}
52
53#[derive(Debug, Clone)]
55pub struct ProgramInfo {
61 pub name: &'static str,
63 pub description: &'static str,
65 pub executable: &'static str,
67 pub features: &'static [&'static str],
69}
70
71#[derive(Debug, Clone)]
73pub struct MethodInfo {
79 pub name: &'static str,
81 pub category: &'static str,
83 pub description: &'static str,
85 pub programs: &'static [&'static str],
87 pub example: Option<&'static str>,
89}
90
91#[derive(Debug, Clone)]
93pub struct FeatureInfo {
98 pub name: &'static str,
100 pub description: &'static str,
102 pub usage: &'static str,
104 pub example: Option<&'static str>,
106}
107
108pub const KEYWORDS: &[Keyword] = &[
110 Keyword {
112 name: "nprocs",
113 category: KeywordCategory::Required,
114 description: "Number of processors for parallel quantum chemistry calculation",
115 default_value: Some("1"),
116 example: Some("nprocs = 30"),
117 required: false,
118 },
119 Keyword {
120 name: "mem",
121 category: KeywordCategory::Required,
122 description: "Memory allocation for QM calculation (format depends on program)",
123 default_value: Some("\"1GB\""),
124 example: Some("mem = \"120GB\" # Gaussian\nmem = \"8000\" # ORCA (MB)"),
125 required: false,
126 },
127 Keyword {
128 name: "method",
129 category: KeywordCategory::Required,
130 description: "Quantum chemistry method and basis set (e.g., 'B3LYP/6-31G*')",
131 default_value: None,
132 example: Some("method = \"B3LYP/6-31G*\"\nmethod = \"n scf(maxcycle=500,xqc) uwb97xd/def2svpp\""),
133 required: true,
134 },
135 Keyword {
136 name: "charge",
137 category: KeywordCategory::Required,
138 description: "Molecular charge for both electronic states",
139 default_value: Some("0"),
140 example: Some("charge = 0\ncharge = 1"),
141 required: false,
142 },
143 Keyword {
144 name: "mult_state_a (or mult_a)",
145 category: KeywordCategory::Required,
146 description: "Spin multiplicity for state A (2S+1, where S is spin)",
147 default_value: Some("1"),
148 example: Some("mult_state_a = 1 # Singlet\nmult_a = 3 # Triplet"),
149 required: false,
150 },
151 Keyword {
152 name: "mult_state_b (or mult_b)",
153 category: KeywordCategory::Required,
154 description: "Spin multiplicity for state B",
155 default_value: Some("1"),
156 example: Some("mult_state_b = 1 # Singlet\nmult_b = 3 # Triplet"),
157 required: false,
158 },
159 Keyword {
160 name: "program",
161 category: KeywordCategory::Required,
162 description: "Quantum chemistry program to use for calculations",
163 default_value: Some("\"gaussian\""),
164 example: Some("program = gaussian\nprogram = orca"),
165 required: false,
166 },
167 Keyword {
168 name: "mode",
169 category: KeywordCategory::Required,
170 description: "Running mode for MECP optimization",
171 default_value: Some("\"normal\""),
172 example: Some("mode = normal\nmode = stable\nmode = read"),
173 required: false,
174 },
175
176 Keyword {
178 name: "td_state_a (or td_a)",
179 category: KeywordCategory::Optional,
180 description: "TD-DFT keywords for state A (Gaussian only, specified in *tail1 for ORCA)",
181 default_value: Some("\".\""),
182 example: Some("td_state_a = \"nstates=10\"\ntd_a = \"nstates=10\""),
183 required: false,
184 },
185 Keyword {
186 name: "td_state_b (or td_b)",
187 category: KeywordCategory::Optional,
188 description: "TD-DFT keywords for state B (Gaussian only, specified in *tail2 for ORCA)",
189 default_value: Some("\".\""),
190 example: Some("td_state_b = \"nstates=10\"\ntd_b = \"nstates=10\""),
191 required: false,
192 },
193 Keyword {
194 name: "mp2",
195 category: KeywordCategory::Optional,
196 description: "Enable MP2 or doubly hybrid DFT calculation (Gaussian only)",
197 default_value: Some("false"),
198 example: Some("mp2 = true"),
199 required: false,
200 },
201 Keyword {
202 name: "bagel_model",
203 category: KeywordCategory::Optional,
204 description: "Path to BAGEL model input file (required when program=bagel)",
205 default_value: None,
206 example: Some("bagel_model = \"model.inp\""),
207 required: false,
208 },
209 Keyword {
210 name: "state_a",
211 category: KeywordCategory::Optional,
212 description: "State index for multireference calculations in BAGEL (0-based) for state A",
213 default_value: Some("0"),
214 example: Some("state_a = 0"),
215 required: false,
216 },
217 Keyword {
218 name: "state_b",
219 category: KeywordCategory::Optional,
220 description: "State index for multireference calculations in BAGEL (0-based) for state B",
221 default_value: Some("1"),
222 example: Some("state_b = 1"),
223 required: false,
224 },
225
226 Keyword {
228 name: "gau_comm",
229 category: KeywordCategory::Program,
230 description: "Gaussian executable command",
231 default_value: Some("\"g16\""),
232 example: Some("gau_comm = \"g16\""),
233 required: false,
234 },
235 Keyword {
236 name: "orca_comm",
237 category: KeywordCategory::Program,
238 description: "ORCA executable command",
239 default_value: Some("\"orca\""),
240 example: Some("orca_comm = \"/opt/orca5/orca\""),
241 required: false,
242 },
243 Keyword {
244 name: "xtb_comm",
245 category: KeywordCategory::Program,
246 description: "xTB executable command",
247 default_value: Some("\"xtb\""),
248 example: Some("xtb_comm = \"xtb\""),
249 required: false,
250 },
251 Keyword {
252 name: "bagel_comm",
253 category: KeywordCategory::Program,
254 description: "BAGEL executable command (includes MPI options)",
255 default_value: Some("\"bagel\""),
256 example: Some("bagel_comm = \"mpirun -np 36 /opt/bagel/bin/BAGEL\""),
257 required: false,
258 },
259
260 Keyword {
262 name: "delta_e",
263 category: KeywordCategory::Convergence,
264 description: "Energy difference threshold (|E1-E2|) for convergence (in Hartree)",
265 default_value: Some("0.000050"),
266 example: Some("delta_e = 0.000050"),
267 required: false,
268 },
269 Keyword {
270 name: "rms_dis",
271 category: KeywordCategory::Convergence,
272 description: "RMS displacement threshold for convergence (in Angstrom)",
273 default_value: Some("0.0025"),
274 example: Some("rms_dis = 0.0025"),
275 required: false,
276 },
277 Keyword {
278 name: "max_dis",
279 category: KeywordCategory::Convergence,
280 description: "Maximum atomic displacement threshold for convergence (in Angstrom)",
281 default_value: Some("0.004"),
282 example: Some("max_dis = 0.004"),
283 required: false,
284 },
285 Keyword {
286 name: "max_grad",
287 category: KeywordCategory::Convergence,
288 description: "Maximum gradient component threshold for convergence (Hartree/Angstrom)",
289 default_value: Some("0.001323"),
290 example: Some("max_grad = 0.001323"),
291 required: false,
292 },
293 Keyword {
294 name: "rms_grad",
295 category: KeywordCategory::Convergence,
296 description: "RMS gradient threshold for convergence (Hartree/Angstrom)",
297 default_value: Some("0.000945"),
298 example: Some("rms_grad = 0.000945"),
299 required: false,
300 },
301 Keyword {
302 name: "max_steps",
303 category: KeywordCategory::Convergence,
304 description: "Maximum number of optimization steps",
305 default_value: Some("100"),
306 example: Some("max_steps = 300"),
307 required: false,
308 },
309 Keyword {
310 name: "max_step_size",
311 category: KeywordCategory::Convergence,
312 description: "Maximum step size for geometry update (in Angstrom)",
313 default_value: Some("0.1"),
314 example: Some("max_step_size = 0.1"),
315 required: false,
316 },
317 Keyword {
318 name: "max_history",
319 category: KeywordCategory::Convergence,
320 description: "Maximum number of history entries for DIIS optimizers (GDIIS/GEDIIS). Controls how many previous iterations are retained for interpolation. Larger values can improve convergence but use more memory.",
321 default_value: Some("4"),
322 example: Some("max_history = 5"),
323 required: false,
324 },
325 Keyword {
326 name: "reduced_factor",
327 category: KeywordCategory::Convergence,
328 description: "Factor for reducing GDIIS step size when RMS gradient is near convergence",
329 default_value: Some("0.5"),
330 example: Some("reduced_factor = 0.5"),
331 required: false,
332 },
333 Keyword {
334 name: "hessian",
335 category: KeywordCategory::Convergence,
336 description: "Hessian update method: direct_psb (default, recommended), inverse_bfgs (Fortran-style), bofill, powell, bfgs_powell_mix. Direct methods required for blend optimizer.",
337 default_value: Some("direct_psb"),
338 example: Some("hessian = direct_psb # Direct H + PSB (recommended, default)\nhessian = inverse_bfgs # Inverse H⁻¹ + BFGS\nhessian = bofill # Bofill update (TS-like)\nhessian = powell # Powell SR1\nhessian = bfgs_powell_mix # Adaptive BFGS/Powell"),
339 required: false,
340 },
341 Keyword {
342 name: "bfgs_rho",
343 category: KeywordCategory::Convergence,
344 description: "Scaling factor for BFGS step (rho). Larger values amplify Newton steps on flat PES.",
345 default_value: Some("15.0"),
346 example: Some("bfgs_rho = 15.0"),
347 required: false,
348 },
349 Keyword {
350 name: "step_reduction_multiplier",
351 category: KeywordCategory::Convergence,
352 description: "Multiplier for RMS gradient threshold in step reduction activation. Step reduction triggers when history norm < rms_g × this value.",
353 default_value: Some("10.0"),
354 example: Some("step_reduction_multiplier = 10.0"),
355 required: false,
356 },
357 Keyword {
358 name: "steepest_descent_step",
359 category: KeywordCategory::Convergence,
360 description: "Small steepest descent step size (Å) for fallback when DIIS/BFGS produces invalid steps",
361 default_value: Some("0.01"),
362 example: Some("steepest_descent_step = 0.01"),
363 required: false,
364 },
365 Keyword {
366 name: "trust_reduction_factor",
367 category: KeywordCategory::Convergence,
368 description: "Trust radius contraction factor when energy increases above threshold",
369 default_value: Some("0.5"),
370 example: Some("trust_reduction_factor = 0.5"),
371 required: false,
372 },
373 Keyword {
374 name: "trust_increase_factor",
375 category: KeywordCategory::Convergence,
376 description: "Trust radius expansion factor when energy decreases below threshold",
377 default_value: Some("1.2"),
378 example: Some("trust_increase_factor = 1.2"),
379 required: false,
380 },
381 Keyword {
382 name: "trust_inc_threshold",
383 category: KeywordCategory::Convergence,
384 description: "Energy increase threshold (Ha) for trust radius reduction. Increase above this triggers contraction.",
385 default_value: Some("0.0001"),
386 example: Some("trust_inc_threshold = 0.0001"),
387 required: false,
388 },
389 Keyword {
390 name: "trust_dec_threshold",
391 category: KeywordCategory::Convergence,
392 description: "Energy decrease threshold (Ha) for trust radius increase. Decrease below this triggers expansion.",
393 default_value: Some("0.0001"),
394 example: Some("trust_dec_threshold = 0.0001"),
395 required: false,
396 },
397 Keyword {
398 name: "trust_min_radius",
399 category: KeywordCategory::Convergence,
400 description: "Minimum allowed trust radius in Å",
401 default_value: Some("0.01"),
402 example: Some("trust_min_radius = 0.01"),
403 required: false,
404 },
405 Keyword {
406 name: "trust_max_radius",
407 category: KeywordCategory::Convergence,
408 description: "Maximum allowed trust radius in Å",
409 default_value: Some("1.0"),
410 example: Some("trust_max_radius = 1.0"),
411 required: false,
412 },
413 Keyword {
414 name: "print_level",
415 category: KeywordCategory::Convergence,
416 description: "Output verbosity: 0=quiet, 1=normal (default), 2=verbose (DIIS debug internals)",
417 default_value: Some("1"),
418 example: Some("print_level = 0 # Quiet\nprint_level = 1 # Normal\nprint_level = 2 # Verbose debug"),
419 required: false,
420 },
421 Keyword {
422 name: "use_gediis",
423 category: KeywordCategory::Convergence,
424 description: "Optimizer variant: false/\"none\"=GDIIS (default), true/\"sequential\"=sequential hybrid GEDIIS, \"blend\"=GDIIS_blend with trust region.",
425 default_value: Some("false"),
426 example: Some("use_gediis = false # GDIIS (default)\nuse_gediis = true # Sequential hybrid GEDIIS\nuse_gediis = sequential # Same as true\nuse_gediis = blend # GDIIS_blend with trust region"),
427 required: false,
428 },
429 Keyword {
430 name: "use_hybrid_gediis",
431 category: KeywordCategory::Convergence,
432 description: "Sequential hybrid GEDIIS/GDIIS (Li & Frisch JCTC 2006). Only active when use_gediis=true or use_gediis=\"sequential\".",
433 default_value: Some("false"),
434 example: Some("use_hybrid_gediis = true # Sequential hybrid\nuse_hybrid_gediis = false # Pure GEDIIS (default for GEDIIS)"),
435 required: false,
436 },
437 Keyword {
438 name: "gediis_blend_mode",
439 category: KeywordCategory::Convergence,
440 description: "Blend strategy when use_gediis=\"blend\": \"fixed\" (50/50), \"fixed_sequential\" (50/50→GDIIS near minimum), \"gradient\" (RMS-gradient weighted), \"sequential\" (GDIIS↔GEDIIS per step).",
441 default_value: Some("\"fixed_sequential\""),
442 example: Some("gediis_blend_mode = fixed # 50/50 GDIIS/GEDIIS blend\ngediis_blend_mode = gradient # RMS-gradient-weighted blend\ngediis_blend_mode = sequential # Per-step GDIIS/GEDIIS switching"),
443 required: false,
444 },
445 Keyword {
446 name: "gediis_switch_rms",
447 category: KeywordCategory::Convergence,
448 description: "RMS gradient threshold for GDIIS→GEDIIS switch. Paper: 10⁻² au ≈ 0.005 Ha/Å.",
449 default_value: Some("0.005"),
450 example: Some("gediis_switch_rms = 0.005"),
451 required: false,
452 },
453 Keyword {
454 name: "gediis_switch_step",
455 category: KeywordCategory::Convergence,
456 description: "RMS displacement threshold for GEDIIS→GDIIS switch. Paper: 2.5×10⁻³ au ≈ 0.001 Å.",
457 default_value: Some("0.001"),
458 example: Some("gediis_switch_step = 0.001"),
459 required: false,
460 },
461 Keyword {
462 name: "switch_step",
463 category: KeywordCategory::Convergence,
464 description: "Step number to switch from BFGS to DIIS optimizers (0=DIIS-only, >=max_steps=BFGS-only)",
465 default_value: Some("3"),
466 example: Some("switch_step = 10 # BFGS for steps 1-10, then DIIS"),
467 required: false,
468 },
469 Keyword {
471 name: "use_robust_diis",
472 category: KeywordCategory::Convergence,
473 description: "Enable DIIS step validation: rejects bad extrapolations via cosine and coefficient checks. Prevents wild steps near convergence.",
474 default_value: Some("false"),
475 example: Some("use_robust_diis = true"),
476 required: false,
477 },
478 Keyword {
479 name: "gediis_variant",
480 category: KeywordCategory::Convergence,
481 description: "GEDIIS variant: auto, rfo, energy, or simultaneous (only with use_robust_diis)",
482 default_value: Some("auto"),
483 example: Some("gediis_variant = energy"),
484 required: false,
485 },
486 Keyword {
487 name: "gdiis_cosine_check",
488 category: KeywordCategory::Convergence,
489 description: "Cosine check mode for GDIIS: none, zero, standard, variable, strict",
490 default_value: Some("standard"),
491 example: Some("gdiis_cosine_check = variable"),
492 required: false,
493 },
494 Keyword {
495 name: "gdiis_coeff_check",
496 category: KeywordCategory::Convergence,
497 description: "Coefficient check mode for GDIIS: none, regular, force_recent, combined",
498 default_value: Some("regular"),
499 example: Some("gdiis_coeff_check = combined"),
500 required: false,
501 },
502 Keyword {
503 name: "n_neg",
504 category: KeywordCategory::Convergence,
505 description: "Number of negative Hessian eigenvalues (0=minimum, 1=TS search)",
506 default_value: Some("0"),
507 example: Some("n_neg = 1 # Transition state search"),
508 required: false,
509 },
510 Keyword {
511 name: "gediis_sim_switch",
512 category: KeywordCategory::Convergence,
513 description: "RMS error threshold for GEDIIS variant switching",
514 default_value: Some("0.0025"),
515 example: Some("gediis_sim_switch = 0.005"),
516 required: false,
517 },
518 Keyword {
520 name: "restart",
521 category: KeywordCategory::Advanced,
522 description: "Restart optimization from checkpoint file",
523 default_value: Some("false"),
524 example: Some("restart = true"),
525 required: false,
526 },
527 Keyword {
528 name: "print_checkpoint",
529 category: KeywordCategory::Advanced,
530 description: "Enable or disable checkpoint JSON file generation (supports true/false, yes/no, 1/0)",
531 default_value: Some("false"),
532 example: Some("print_checkpoint = false"),
533 required: false,
534 },
535 Keyword {
536 name: "fixedatoms",
537 category: KeywordCategory::Constraints,
538 description: "Comma-separated list or ranges of atom indices to freeze (1-based, converted to 0-based internally)",
539 default_value: None,
540 example: Some("fixedatoms = \"1,5,10\" # atoms 1, 5, and 10\nfixedatoms = \"1-5,10-15\" # ranges"),
541 required: false,
542 },
543 Keyword {
544 name: "fix_de",
545 category: KeywordCategory::Advanced,
546 description: "Fix energy difference to target value (in eV) for specialized optimizations",
547 default_value: Some("0.0"),
548 example: Some("fix_de = 2.5 # Target ΔE = 2.5 eV"),
549 required: false,
550 },
551
552 Keyword {
554 name: "drive_coordinate",
555 category: KeywordCategory::Advanced,
556 description: "Coordinate specification for reaction path following",
557 default_value: None,
558 example: Some("drive_coordinate = \"R 1 2\" # Distance between atoms 1-2"),
559 required: false,
560 },
561 Keyword {
562 name: "drive_start",
563 category: KeywordCategory::Advanced,
564 description: "Starting value for coordinate driving (in Angstrom or degrees)",
565 default_value: Some("0.0"),
566 example: Some("drive_start = 1.0"),
567 required: false,
568 },
569 Keyword {
570 name: "drive_end",
571 category: KeywordCategory::Advanced,
572 description: "Ending value for coordinate driving (in Angstrom or degrees)",
573 default_value: Some("0.0"),
574 example: Some("drive_end = 2.0"),
575 required: false,
576 },
577 Keyword {
578 name: "drive_steps",
579 category: KeywordCategory::Advanced,
580 description: "Number of steps for coordinate driving",
581 default_value: Some("10"),
582 example: Some("drive_steps = 20"),
583 required: false,
584 },
585 Keyword {
586 name: "drive_type",
587 category: KeywordCategory::Advanced,
588 description: "Type of coordinate driving",
589 default_value: None,
590 example: Some("drive_type = \"bond\" # Bond distance\ndrive_type = \"angle\" # Bond angle"),
591 required: false,
592 },
593 Keyword {
594 name: "drive_atoms",
595 category: KeywordCategory::Advanced,
596 description: "Comma-separated atom indices for driving (1-based)",
597 default_value: None,
598 example: Some("drive_atoms = \"1,2\" # For bond/angle\ndrive_atoms = \"1,2,3\" # For angle"),
599 required: false,
600 },
601
602 Keyword {
604 name: "isoniom",
605 category: KeywordCategory::Advanced,
606 description: "Enable ONIOM QM/MM embedding",
607 default_value: Some("false"),
608 example: Some("isoniom = true"),
609 required: false,
610 },
611 Keyword {
612 name: "chargeandmultforoniom1",
613 category: KeywordCategory::Advanced,
614 description: "Charge and multiplicity for ONIOM layer 1",
615 default_value: None,
616 example: Some("chargeandmultforoniom1 = \"0 1\""),
617 required: false,
618 },
619 Keyword {
620 name: "chargeandmultforoniom2",
621 category: KeywordCategory::Advanced,
622 description: "Charge and multiplicity for ONIOM layer 2",
623 default_value: None,
624 example: Some("chargeandmultforoniom2 = \"0 1\""),
625 required: false,
626 },
627
628 Keyword {
630 name: "custom_interface_file",
631 category: KeywordCategory::Advanced,
632 description: "JSON configuration file for custom QM program interface",
633 default_value: None,
634 example: Some("custom_interface_file = \"custom.json\""),
635 required: false,
636 },
637 Keyword {
638 name: "basis",
639 category: KeywordCategory::Advanced,
640 description: "Basis set specification (for programs that separate method and basis)",
641 default_value: None,
642 example: Some("basis = \"def2-TZVP\""),
643 required: false,
644 },
645 Keyword {
646 name: "solvent",
647 category: KeywordCategory::Advanced,
648 description: "Solvent model specification (e.g., PCM, SMD)",
649 default_value: None,
650 example: Some("solvent = \"smd=water\""),
651 required: false,
652 },
653 Keyword {
654 name: "dispersion",
655 category: KeywordCategory::Advanced,
656 description: "Dispersion correction specification",
657 default_value: None,
658 example: Some("dispersion = \"GD3BJ\""),
659 required: false,
660 },
661 Keyword {
662 name: "charge_b",
663 category: KeywordCategory::Advanced,
664 description: "Separate molecular charge for state B (defaults to charge value)",
665 default_value: None,
666 example: Some("charge = 1\ncharge_b = -1 # State B has different charge"),
667 required: false,
668 },
669];
670
671pub fn get_programs() -> &'static [ProgramInfo] {
673 &[
674 ProgramInfo {
675 name: "Gaussian",
676 description: "Most widely used quantum chemistry program with extensive functionality",
677 executable: "g16 (or g09, g03)",
678 features: &[
679 "DFT, HF, post-HF methods",
680 "TD-DFT excited states",
681 "MP2 and higher",
682 "Solvent models (PCM, SMD)",
683 "ONIOM QM/MM",
684 "Force calculations",
685 "Frequency analysis",
686 ],
687 },
688 ProgramInfo {
689 name: "ORCA",
690 description: "Modern quantum chemistry program with efficient algorithms",
691 executable: "orca",
692 features: &[
693 "DFT, HF, post-HF methods",
694 "TD-DFT excited states",
695 "Strong correlation (CASSCF, CASPT2)",
696 "Solvent models",
697 "Relativistic corrections",
698 "EPR/NMR properties",
699 ],
700 },
701 ProgramInfo {
702 name: "xTB",
703 description: "Semi-empirical tight-binding program for large systems",
704 executable: "xtb",
705 features: &[
706 "GFN2-xTB method",
707 "Very fast for large systems",
708 "Semi-empirical accuracy",
709 "GPU acceleration",
710 "Solvation models",
711 ],
712 },
713 ProgramInfo {
714 name: "BAGEL",
715 description: "Multireference quantum chemistry program for strong correlation",
716 executable: "mpirun -np N /path/to/BAGEL",
717 features: &[
718 "CASSCF/CASPT2",
719 "MRCI methods",
720 "Strongly correlated systems",
721 "Excited states",
722 "State-specific optimizations",
723 ],
724 },
725 ProgramInfo {
726 name: "Custom",
727 description: "User-defined QM program via JSON configuration",
728 executable: "User-specified",
729 features: &[
730 "Any program with text I/O",
731 "Regex-based parsing",
732 "Configurable via JSON",
733 "Supports forces and energies",
734 ],
735 },
736 ]
737}
738
739pub fn get_methods() -> &'static [MethodInfo] {
741 &[
742 MethodInfo {
743 name: "B3LYP",
744 category: "DFT",
745 description: "Hybrid DFT functional, widely used for general chemistry",
746 programs: &["Gaussian", "ORCA", "xTB"],
747 example: Some("method = \"B3LYP/6-31G*\""),
748 },
749 MethodInfo {
750 name: "wB97XD",
751 category: "DFT",
752 description: "Long-range corrected DFT functional with dispersion",
753 programs: &["Gaussian"],
754 example: Some("method = \"wB97XD/def2-SVP\""),
755 },
756 MethodInfo {
757 name: "PBE0",
758 category: "DFT",
759 description: "Hybrid DFT functional with 25% exact exchange",
760 programs: &["Gaussian", "ORCA"],
761 example: Some("method = \"PBE0/def2-TZVP\""),
762 },
763 MethodInfo {
764 name: "MP2",
765 category: "Post-HF",
766 description: "Second-order Møller-Plesset perturbation theory",
767 programs: &["Gaussian", "ORCA"],
768 example: Some("mp2 = true # In Gaussian"),
769 },
770 MethodInfo {
771 name: "CASSCF",
772 category: "Multireference",
773 description: "Complete Active Space Self-Consistent Field",
774 programs: &["ORCA", "BAGEL"],
775 example: Some("# In ORCA: %casscf nel 6 norb 6 end"),
776 },
777 MethodInfo {
778 name: "GFN2-xTB",
779 category: "Semi-empirical",
780 description: "Semi-empirical tight-binding method for large systems",
781 programs: &["xTB"],
782 example: Some("# Automatically used with xTB program"),
783 },
784 ]
785}
786
787pub const FEATURES: &[FeatureInfo] = &[
789 FeatureInfo {
790 name: "Normal Mode",
791 description: "Standard MECP optimization between two electronic states",
792 usage: "mode = normal",
793 example: Some("mode = normal"),
794 },
795 FeatureInfo {
796 name: "Stability Analysis",
797 description: "Check wavefunction stability before optimization",
798 usage: "mode = stable",
799 example: Some("mode = stable # Runs stability check first"),
800 },
801 FeatureInfo {
802 name: "Restart",
803 description: "Restart optimization from saved checkpoint",
804 usage: "restart = true",
805 example: Some("checkpoint = \"restart.chk\"\nrestart = true"),
806 },
807 FeatureInfo {
808 name: "Configuration File (omecp_config.cfg)",
809 description: "Hierarchical configuration system with local/user/system precedence",
810 usage: "Create template: omecp ci omecp_config.cfg",
811 example: Some("# See omecp_config.cfg template for all options"),
812 },
813 FeatureInfo {
814 name: "Parameter Display",
815 description: "Automatic display of all configuration parameters and settings source at startup",
816 usage: "Enabled by default, shows which config file is loaded",
817 example: Some("Displays: source file, all parameters, thresholds, settings"),
818 },
819 FeatureInfo {
820 name: "Debug Logging",
821 description: "Optional file-based debug logging with dynamic filenames",
822 usage: "Enable in omecp_config.cfg: file_logging = true",
823 example: Some("Creates: omecp_debug_<input_basename>.log"),
824 },
825 FeatureInfo {
826 name: "BFGS Optimizer",
827 description: "Quasi-Newton method used for first 3 steps",
828 usage: "Used automatically for initial steps",
829 example: None,
830 },
831 FeatureInfo {
832 name: "GDIIS Optimizer",
833 description: "Geometry Direct Inversion of Iterative Subspace for later steps",
834 usage: "use_gediis = false (default)",
835 example: Some("use_gediis = false"),
836 },
837 FeatureInfo {
838 name: "GEDIIS Optimizer",
839 description: "Energy-informed DIIS with diagonal energy coupling (Li & Frisch JCTC 2006)",
840 usage: "use_gediis = true",
841 example: Some("use_gediis = true # Enables GEDIIS"),
842 },
843 FeatureInfo {
844 name: "Sequential Hybrid GEDIIS",
845 description: "3-phase switching: GDIIS → GEDIIS → GDIIS (Li & Frisch JCTC 2006)",
846 usage: "use_gediis = true + use_hybrid_gediis = true",
847 example: Some("use_hybrid_gediis = true # Sequential hybrid"),
848 },
849 FeatureInfo {
850 name: "GDIIS_blend Optimizer",
851 description: "Gradient-weighted and trust-region blend of GDIIS/GEDIIS steps (experimental).",
852 usage: "use_gediis = blend + use_hybrid_gediis = true + gediis_blend_mode = <mode>",
853 example: Some("use_gediis = blend\nuse_hybrid_gediis = true\ngediis_blend_mode = gradient # RMS-gradient-weighted blend"),
854 },
855 FeatureInfo {
856 name: "Flexible Optimizer Switching",
857 description: "Control when to switch from BFGS to DIIS optimizers",
858 usage: "switch_step = N (default: 3)",
859 example: Some("switch_step = 0 # DIIS-only\nswitch_step = 10 # BFGS for 10 steps\nswitch_step = 999 # BFGS-only"),
860 },
861 FeatureInfo {
862 name: "Bond Constraints",
863 description: "Fix bond distances during optimization",
864 usage: "R i j value (in Angstrom)",
865 example: Some("R 1 2 1.5 # Fix distance between atoms 1-2 to 1.5 Angstrom"),
866 },
867 FeatureInfo {
868 name: "Angle Constraints",
869 description: "Fix bond angles during optimization",
870 usage: "A i j k value (in degrees)",
871 example: Some("A 1 2 3 109.5 # Fix angle 1-2-3 to 109.5°"),
872 },
873 FeatureInfo {
874 name: "PES Scanning",
875 description: "Scan potential energy surface along 1D or 2D coordinate",
876 usage: "S R i j start points step OR S A i j k start points step",
877 example: Some("S R 1 2 1.0 10 0.1 # Scan R(1,2) from 1.0 Angstrom, 10 steps"),
878 },
879 FeatureInfo {
880 name: "Coordinate Driving",
881 description: "Drive along reaction coordinate",
882 usage: "drive_type, drive_atoms, drive_start, drive_end, drive_steps",
883 example: Some("drive_type = \"bond\"\ndrive_atoms = \"1,2\"\ndrive_start = 1.0\ndrive_end = 2.0"),
884 },
885 FeatureInfo {
886 name: "LST Interpolation",
887 description: "Linear/Quadratic Synchronous Transit interpolation",
888 usage: "Provide *lst1 and *lst2 geometries in input file",
889 example: Some("*lst1\n# Geometry 1\n*\n*lst2\n# Geometry 2\n*"),
890 },
891 FeatureInfo {
892 name: "External Geometry",
893 description: "Load geometry from external file (.xyz, .log, .gjf)",
894 usage: "@filename in *geom section",
895 example: Some("*geom\n@/path/to/molecule.xyz\n*"),
896 },
897 FeatureInfo {
898 name: "Fixed Atoms",
899 description: "Freeze specific atoms during optimization",
900 usage: "fixedatoms = \"list\"",
901 example: Some("fixedatoms = \"1,2,3\" # Fix first 3 atoms"),
902 },
903 FeatureInfo {
904 name: "ONIOM QM/MM",
905 description: "Embed molecule in MM environment",
906 usage: "isoniom = true with layer definitions",
907 example: Some("isoniom = true\nchargeandmultforoniom1 = \"0 1\""),
908 },
909 FeatureInfo {
910 name: "Custom Interface",
911 description: "Define custom QM program via JSON configuration",
912 usage: "custom_interface_file = \"config.json\"",
913 example: Some("# See documentation for JSON format"),
914 },
915];
916
917pub fn print_header() {
930 println!("**** OpenMECP: Minimum Energy Crossing Point Optimizer ****");
931 println!(
932 " Version {} Release date: 2026",
933 env!("CARGO_PKG_VERSION")
934 );
935 println!(" ****Developer Le Nhan Pham**** ");
936 println!(" https://github.com/lenhanpham/OpenMECP \n");
937 println!(" Please cite this preprint if you use OpenMECP for your research:");
938 println!(" #-------------------------------------------------------------------------------#");
939 println!(" # L.N Pham, \"OpenMECP: A High-Performance Rust Implementation for #");
940 println!(" # the Rigorous Location of Minimum Energy Crossing Points in Chemical Dynamics\" #");
941 println!(" # 2026, https://doi.org/10.13140/RG.2.2.21309.73443 #");
942 println!(" #-------------------------------------------------------------------------------#");
943}
944
945pub fn print_global_help() {
947 print_header();
948 println!();
949 println!();
950 println!("USAGE:");
951 println!(" omecp [OPTIONS] <COMMAND>");
952 println!();
953 println!("COMMANDS:");
954 println!(" ci <geometry_file> [output_file]");
955 println!(" Create a template input file from a geometry file");
956 println!(" Supported formats: .xyz, .log, .gjf");
957 println!();
958 println!(" ci omecp_config.cfg");
959 println!(" Create a configuration template file");
960 println!(" See 'Configuration File' section below");
961 println!();
962 println!(" <input_file>");
963 println!(" Run MECP optimization using the input file");
964 println!();
965 println!("OPTIONS:");
966 println!(" -h, --help [topic] Show help. Topics: keywords, methods, features, examples");
967 println!();
968 println!("CONFIGURATION FILE:");
969 println!(" OpenMECP uses 'omecp_config.cfg' for program configuration.");
970 println!(" Create template: omecp ci omecp_config.cfg");
971 println!(" Supported locations:");
972 println!(" - ./omecp_config.cfg (local, highest priority)");
973 println!(" - ~/.config/omecp/omecp_config.cfg (user)");
974 println!(" - /etc/omecp/omecp_config.cfg (system)");
975 println!(" Features: file extensions, logging, cleanup, debug output");
976 println!(" Parameters are automatically displayed at startup");
977 println!();
978 println!("EXAMPLES:");
979 println!(" Create template: omecp ci molecule.xyz");
980 println!(" Create with name: omecp ci molecule.xyz custom.inp");
981 println!(" Run MECP: omecp input.inp > output.log");
982 println!(" Create settings: omecp ci omecp_config.cfg");
983 println!(" View keywords: omecp --help keywords");
984 println!(" View methods: omecp --help methods");
985 println!(" View features: omecp --help features");
986 println!();
987}
988
989pub fn print_ci_help() {
991 println!("Create Input Template (ci) Command");
992 println!("═════════════════════════════════════");
993 println!();
994 println!("USAGE:");
995 println!(" omecp ci <geometry_file> [output_file]");
996 println!();
997 println!("DESCRIPTION:");
998 println!(" Generates a template MECP input file from a geometry file.");
999 println!(" The template includes all required and optional parameters with");
1000 println!(" sensible defaults, ready for customization.");
1001 println!();
1002 println!("ARGUMENTS:");
1003 println!(" <geometry_file> Input geometry file (required)");
1004 println!(" Supported formats:");
1005 println!(" - .xyz: XYZ coordinate file");
1006 println!(" - .gjf: Gaussian input file");
1007 println!(" - .log: Gaussian output file");
1008 println!();
1009 println!(" [output_file] Output template file (optional)");
1010 println!(" Default naming:");
1011 println!(" - .xyz/.gjf files: <name>.inp");
1012 println!(" - .log/.out/.json files: <name>_omecp.inp");
1013 println!(" (suffix prevents overwriting QM output)");
1014 println!();
1015 println!("EXAMPLES:");
1016 println!(" omecp ci molecule.xyz");
1017 println!(" Creates 'molecule.inp' with '@molecule.xyz' reference");
1018 println!();
1019 println!(" omecp ci calc.log");
1020 println!(" Creates 'calc_omecp.inp' (prevents overwriting calc.log)");
1021 println!();
1022 println!(" omecp ci calc.log custom.inp");
1023 println!(" Creates 'custom.inp' with '@calc.log' reference");
1024 println!();
1025 println!("OUTPUT FORMAT:");
1026 println!(" Generated template includes:");
1027 println!(" - Required parameters (nprocs, mem, method, charge, mult, mode)");
1028 println!(" - Optional parameters with defaults");
1029 println!(" - Convergence thresholds");
1030 println!(" - Program settings");
1031 println!(" - *geom section with @reference");
1032 println!(" - Empty *tail1 and *tail2 sections");
1033 println!(" - Constraint examples");
1034 println!();
1035}
1036
1037pub fn print_keyword_help() {
1039 println!("KEYWORD REFERENCE");
1040 println!("═══════════════════════════════════════════════════════════════════════");
1041 println!();
1042
1043 let mut categories: HashMap<KeywordCategory, Vec<&Keyword>> = HashMap::new();
1045 for keyword in KEYWORDS {
1046 categories
1047 .entry(keyword.category)
1048 .or_default()
1049 .push(keyword);
1050 }
1051
1052 for (category, keywords) in categories {
1054 print_category_header(category);
1055 println!();
1056
1057 for keyword in keywords {
1058 print_keyword(keyword);
1059 println!();
1060 }
1061 println!();
1062 }
1063}
1064
1065pub fn print_method_help() {
1067 println!("METHOD REFERENCE");
1068 println!("═══════════════════════════════════════════════════════════════════════");
1069 println!();
1070 println!("OpenMECP supports all quantum chemistry methods available in the");
1071 println!("underlying QM program (Gaussian, ORCA, xTB, BAGEL). The methods listed");
1072 println!("below are typical examples — any functional, basis set, and method");
1073 println!("combination supported by your QM program can be used.");
1074 println!();
1075
1076 let methods = get_methods();
1077
1078 let mut categories: HashMap<&str, Vec<&MethodInfo>> = HashMap::new();
1080 for method in methods {
1081 categories.entry(method.category).or_default().push(method);
1082 }
1083
1084 for (category, methods) in categories {
1085 println!("{}", category.to_uppercase());
1086 println!("{}", "─".repeat(76));
1087 println!();
1088
1089 for method in methods {
1090 println!("{}", method.name);
1091 println!(" {}", method.description);
1092 println!(" Programs: {}", method.programs.join(", "));
1093 if let Some(example) = method.example {
1094 println!(" Example: {}", example);
1095 }
1096 println!();
1097 }
1098 println!();
1099 }
1100
1101 println!("SUPPORTED PROGRAMS");
1102 println!("{}", "─".repeat(76));
1103 println!();
1104
1105 for program in get_programs() {
1106 println!("{}", program.name);
1107 println!(" {}", program.description);
1108 println!(" Executable: {}", program.executable);
1109 println!(" Features:");
1110 for feature in program.features {
1111 println!(" • {}", feature);
1112 }
1113 println!();
1114 }
1115}
1116
1117pub fn print_feature_help() {
1119 println!("FEATURE REFERENCE");
1120 println!("═══════════════════════════════════════════════════════════════════════");
1121 println!();
1122
1123 for feature in FEATURES {
1124 println!("{}", feature.name);
1125 println!(" {}", feature.description);
1126 println!(" Usage: {}", feature.usage);
1127 if let Some(example) = feature.example {
1128 println!(" Example: {}", example);
1129 }
1130 println!();
1131 }
1132
1133 println!("RUN MODES");
1134 println!("{}", "─".repeat(76));
1135 println!();
1136 println!("normal");
1137 println!(" Standard MECP optimization mode. Runs single-point calculations");
1138 println!(" and optimizes geometry to find minimum energy crossing point.");
1139 println!();
1140 println!("stable");
1141 println!(" Pre-point stability analysis. Checks wavefunction stability before");
1142 println!(" optimization. Useful for avoiding convergence to saddle points.");
1143 println!();
1144 println!("read");
1145 println!(" Restart mode with wavefunction reading. Uses saved checkpoint to");
1146 println!(" continue previous optimization or start from known wavefunction.");
1147 println!();
1148 println!("noread");
1149 println!(" Fresh start without reading wavefunction. Useful when initial");
1150 println!(" guess is poor or when switching between methods.");
1151 println!();
1152 println!("inter_read");
1153 println!(" Intermediate read mode. Copies wavefunction from state B to state A");
1154 println!(" with guess=mix for better convergence in Gaussian.");
1155 println!();
1156 println!("coordinate_drive");
1157 println!(" Drives the system along a specified reaction coordinate without");
1158 println!(" full optimization. Useful for exploring reaction paths.");
1159 println!();
1160 println!("fix_de");
1161 println!(" Fix-dE optimization. Constrains the energy difference between states");
1162 println!(" to a target value while optimizing geometry.");
1163 println!();
1164
1165 println!("OPTIMIZATION ALGORITHMS");
1166 println!("{}", "─".repeat(76));
1167 println!();
1168 println!("BFGS");
1169 println!(" Broyden-Fletcher-Goldfarb-Shanno quasi-Newton method. Used for the");
1170 println!(" first 3 optimization steps when Hessian is not available.");
1171 println!();
1172 println!("GDIIS (default)");
1173 println!(" Geometry Direct Inversion of Iterative Subspace. Robust convergence");
1174 println!(" for MECP using Newton-step error vectors. Default optimizer.");
1175 println!();
1176 println!("GEDIIS");
1177 println!(" Energy-informed DIIS (Li & Frisch JCTC 2006). Uses GDIIS-compatible");
1178 println!(" error vectors (e_i = H⁻¹·(g_vec+f_vec)) with a diagonal energy coupling");
1179 println!(" B[i,i] += α·E_i² that biases interpolation toward low-gap points.");
1180 println!(" Enforces interpolation (c_i > 0) for stability. Enable: 'use_gediis = true'.");
1181 println!();
1182 println!("Sequential Hybrid");
1183 println!(" 3-phase switching strategy (Li & Frisch JCTC 2006 Sec II.B):");
1184 println!(" Phase 1: GDIIS (pre-optimizer)");
1185 println!(" Phase 2: GEDIIS when RMS gradient < gediis_switch_rms (default 0.005 Ha/Å)");
1186 println!(" Phase 3: GDIIS when RMS step < gediis_switch_step (default 0.001 Å)");
1187 println!(" Enable: 'use_gediis = true' + 'use_hybrid_gediis = true'.");
1188 println!(" Configurable thresholds: gediis_switch_rms, gediis_switch_step.");
1189 println!();
1190 println!("GDIIS_blend");
1191 println!(" Gradient-weighted and trust-region blend of GDIIS/GEDIIS steps.");
1192 println!(" Four blend modes (gediis_blend_mode):");
1193 println!(" - 'fixed' (50/50): Equal weight to GDIIS and GEDIIS steps.");
1194 println!(" - 'fixed_sequential' (default): 50/50 blend far from minimum,");
1195 println!(" automatically transitions to pure GDIIS near convergence");
1196 println!(" (RMS displacement < gediis_switch_step).");
1197 println!(" - 'gradient': Smooth blend weight w = RMS_grad / (RMS_grad + gediis_switch_rms)");
1198 println!(" EDIIS-dominated when far from minimum (energy-guided navigation),");
1199 println!(" smoothly transitions to GDIIS-dominated near convergence (Newton step).");
1200 println!(" - 'sequential': Per-step switching based on RMS displacement trend.");
1201 println!(" All modes use a trust radius that contracts on energy rise and");
1202 println!(" expands on energy drop. Enable: 'use_gediis = blend'.");
1203 println!();
1204 println!("OPTIMIZER SWITCHING");
1205 println!(" Control when to switch from BFGS to DIIS with 'switch_step = N':");
1206 println!(" - switch_step = 0: Pure DIIS (no BFGS)");
1207 println!(" - switch_step = 3: Default hybrid (BFGS steps 1-3, then DIIS)");
1208 println!(" - switch_step = 10: Extended BFGS (steps 1-10, then DIIS)");
1209 println!(" - switch_step >= max_steps: Pure BFGS (no DIIS)");
1210 println!();
1211
1212 println!("CONSTRAINT TYPES");
1213 println!("{}", "─".repeat(76));
1214 println!();
1215 println!("Bond Constraints (R)");
1216 println!(" Fix distance between two atoms:");
1217 println!(" R i j value # value in Angstrom, atoms are 1-based");
1218 println!();
1219 println!("Angle Constraints (A)");
1220 println!(" Fix angle formed by three atoms:");
1221 println!(" A i j k value # value in degrees, atoms are 1-based");
1222 println!();
1223 println!("Fixed Atoms");
1224 println!(" Freeze specific atoms during optimization:");
1225 println!(" fixedatoms = \"1,2,3\"");
1226 println!(" fixedatoms = \"1-5,10-15\" # Can use ranges");
1227 println!();
1228 println!("PES Scans");
1229 println!(" Scan along 1D or 2D coordinate:");
1230 println!(" S R i j start num_points step_size");
1231 println!(" S A i j k start num_points step_size");
1232 println!();
1233}
1234
1235pub fn print_examples() {
1237 println!("USAGE EXAMPLES");
1238 println!("═══════════════════════════════════════════════════════════════════════");
1239 println!();
1240
1241 println!("BASIC USAGE");
1242 println!("{}", "─".repeat(76));
1243 println!();
1244 println!("1. Create template from XYZ file:");
1245 println!(" $ omecp ci water.xyz");
1246 println!(" → Creates water.inp with @water.xyz reference");
1247 println!();
1248 println!("2. Run MECP optimization:");
1249 println!(" $ omecp calculation.inp");
1250 println!();
1251 println!("3. View help:");
1252 println!(" $ omecp --help");
1253 println!(" $ omecp --help keywords");
1254 println!();
1255
1256 println!("INPUT FILE EXAMPLE");
1257 println!("{}", "─".repeat(76));
1258 println!();
1259 println!("# Required parameters");
1260 println!("nprocs = 30");
1261 println!("mem = 120GB");
1262 println!("method = n scf(maxcycle=500,xqc) uwb97xd/def2svpp scrf=(smd,solvent=acetonitrile)");
1263 println!("charge = 1");
1264 println!("mult_state_a = 3 # or mult_a = 3");
1265 println!("mult_state_b = 1 # or mult_b = 1");
1266 println!("mode = normal");
1267 println!();
1268 println!("# Optional convergence thresholds");
1269 println!("delta_e = 0.000050");
1270 println!("rms_dis = 0.0025");
1271 println!("max_history = 4");
1272 println!();
1273 println!("# Program settings");
1274 println!("program = gaussian");
1275 println!("gau_comm = g16");
1276 println!();
1277 println!("# Geometry section");
1278 println!("*geom");
1279 println!("@molecule.xyz # Or inline coordinates");
1280 println!("C 0.0 0.0 0.0");
1281 println!("H 0.0 0.0 1.0");
1282 println!("*");
1283 println!();
1284 println!("# Constraints (optional)");
1285 println!("*constr");
1286 println!("R 1 2 1.0 # Fix bond distance");
1287 println!("*");
1288 println!();
1289
1290 println!("ADVANCED EXAMPLES");
1291 println!("{}", "─".repeat(76));
1292 println!();
1293 println!("1. Stability analysis mode:");
1294 println!(" mode = stable");
1295 println!();
1296 println!("2. Restart from checkpoint:");
1297 println!(" checkpoint = \"restart.chk\"");
1298 println!(" restart = true");
1299 println!();
1300 println!("3. Use custom QM program:");
1301 println!(" program = custom");
1302 println!(" custom_interface_file = \"my_qm.json\"");
1303 println!();
1304 println!("4. Coordinate driving:");
1305 println!(" mode = coordinate_drive");
1306 println!(" drive_type = \"bond\"");
1307 println!(" drive_atoms = \"1,2\"");
1308 println!(" drive_start = 1.0");
1309 println!(" drive_end = 2.0");
1310 println!(" drive_steps = 10");
1311 println!();
1312 println!("5. PES scan:");
1313 println!(" # Add to *constr section:");
1314 println!(" S R 1 2 1.0 10 0.1");
1315 println!();
1316 println!("6. Fix energy difference:");
1317 println!(" mode = fix_de");
1318 println!(" fix_de = 2.5 # Target ΔE in eV");
1319 println!();
1320
1321 println!("TROUBLESHOOTING");
1322 println!("{}", "─".repeat(76));
1323 println!();
1324 println!("• Convergence problems:");
1325 println!(" - Increase max_steps");
1326 println!(" - Relax convergence thresholds");
1327 println!(" - Try stable mode for wavefunction check");
1328 println!(" - Use smaller max_step_size");
1329 println!();
1330 println!("• Wrong spin state:");
1331 println!(" - Check mult_state_a and mult_state_b values");
1332 println!(" - Use inter_read mode in Gaussian");
1333 println!();
1334 println!("• Large systems:");
1335 println!(" - Use xTB program for faster calculations");
1336 println!(" - Freeze non-reactive atoms with fixedatoms");
1337 println!(" - Use lower-cost methods for pre-optimization");
1338 println!();
1339}
1340
1341fn print_category_header(category: KeywordCategory) {
1343 match category {
1344 KeywordCategory::Required => {
1345 println!("REQUIRED PARAMETERS");
1346 println!("{}", "─".repeat(76));
1347 }
1348 KeywordCategory::Optional => {
1349 println!("OPTIONAL PARAMETERS");
1350 println!("{}", "─".repeat(76));
1351 }
1352 KeywordCategory::Convergence => {
1353 println!("CONVERGENCE THRESHOLDS");
1354 println!("{}", "─".repeat(76));
1355 }
1356 KeywordCategory::Program => {
1357 println!("PROGRAM COMMANDS");
1358 println!("{}", "─".repeat(76));
1359 }
1360 KeywordCategory::TdDft => {
1361 println!("TD-DFT PARAMETERS");
1362 println!("{}", "─".repeat(76));
1363 }
1364 KeywordCategory::Constraints => {
1365 println!("CONSTRAINT PARAMETERS");
1366 println!("{}", "─".repeat(76));
1367 }
1368 KeywordCategory::Advanced => {
1369 println!("ADVANCED PARAMETERS");
1370 println!("{}", "─".repeat(76));
1371 }
1372 }
1373}
1374
1375fn print_keyword(keyword: &Keyword) {
1377 let required_str = if keyword.required { " [REQUIRED]" } else { "" };
1378
1379 println!("{}{}", keyword.name, required_str);
1380 println!(" {}", keyword.description);
1381
1382 if let Some(default) = keyword.default_value {
1383 println!(" Default: {}", default);
1384 }
1385
1386 if let Some(example) = keyword.example {
1387 println!(" Example: {}", example);
1388 }
1389}