cp2k-rs 0.2.3

Rust bindings for CP2K with Python interface
Documentation
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
//! IPC protocol types for the CP2K worker process.
//!
//! Serialization uses bincode for compact binary encoding.
//! All messages are length-prefixed: [u32 length][payload bytes].

use serde::{Deserialize, Serialize};

// K-Point configuration types
// ─── CP2K input specification ────────────────────────────────────────────────

/// How the CP2K worker should obtain the `.inp` file.
///
/// This is the "source of truth" input mechanism: either the user supplies a
/// path (`FromFile`) or a typed settings model is rendered into an input file
/// (`Generated`).
///
/// Note: The worker ultimately needs a file path for CP2K. For `Generated`,
/// the worker is expected to render settings to a temporary `.inp` file.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(clippy::large_enum_variant)]
pub enum Cp2kInputSpec {
    /// Use an existing CP2K input file on disk.
    FromFile { path: String },
    /// Generate an input file from typed settings.
    Generated { settings: Cp2kSettings },
}

/// A minimal, typed CP2K settings model.
///
/// This is intentionally scoped to the most common Quickstep/DFT parameters we
/// want to support without requiring a handwritten `.inp` file.
///
/// You can always extend this model over time.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kSettings {
    /// Settings for the `&GLOBAL` section.
    pub global: Option<Cp2kGlobalSettings>,
    /// Settings for `&FORCE_EVAL` (only the common `METHOD Quickstep` path is
    /// addressed initially).
    pub force_eval: Cp2kForceEvalSettings,
}

/// Settings for `&GLOBAL`.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kGlobalSettings {
    /// `PROJECT` label.
    pub project: Option<String>,
    /// `PRINT_LEVEL`
    pub print_level: Option<String>,
    /// `RUN_TYPE`
    pub run_type: Option<String>,
    /// `WALLTIME` (CP2K string, e.g. "00:59:00")
    pub walltime: Option<String>,
    /// `SEED` values. CP2K accepts either one integer or multiple integers.
    pub seed: Option<Vec<i32>>,
    /// `ECHO_INPUT`
    pub echo_input: Option<bool>,
}

/// Settings for a `&FORCE_EVAL` section, focusing on Quickstep/DFT.
///
/// `&SUBSYS` geometry (`&CELL` and `&COORD`) is always injected from the frontend
/// geometry and is therefore not part of this settings struct.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Cp2kForceEvalSettings {
    /// `METHOD` (commonly "Quickstep").
    pub method: String,
    /// Optional stress tensor request (e.g. "ANALYTICAL").
    pub stress_tensor: Option<String>,
    /// DFT settings (for `METHOD Quickstep`).
    pub dft: Option<Cp2kDftSettings>,
    /// Subsystem settings (e.g. `&KIND` blocks).
    pub subsys: Option<Cp2kSubsysSettings>,
}

impl Default for Cp2kForceEvalSettings {
    fn default() -> Self {
        Self {
            method: "Quickstep".to_string(),
            stress_tensor: None,
            dft: Some(Cp2kDftSettings::default()),
            subsys: None,
        }
    }
}

/// Settings for `&SUBSYS` (excluding geometry injection).
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kSubsysSettings {
    /// `&KIND` blocks for element-specific basis/potential.
    pub kinds: Vec<Cp2kKindSettings>,
    /// Extra subsections to be appended under `&SUBSYS`.
    pub extra_sections: Vec<Cp2kSection>,
    /// Extra keywords to be appended under `&SUBSYS`.
    pub extra_keywords: Vec<Cp2kKeyword>,
}

/// Settings for a `&KIND` block (e.g. `&KIND Si`).
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kKindSettings {
    /// Element symbol, used as the `&KIND` parameter.
    pub element: String,
    /// `BASIS_SET` keyword.
    pub basis_set: Option<String>,
    /// `POTENTIAL` keyword.
    pub potential: Option<String>,
    /// Extra keywords inside the `&KIND` block.
    pub extra_keywords: Vec<Cp2kKeyword>,
    /// Extra subsections inside the `&KIND` block.
    pub extra_sections: Vec<Cp2kSection>,
}

/// Settings for `&DFT` (Quickstep).
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kDftSettings {
    pub basis_set_file_name: Option<String>,
    pub potential_file_name: Option<String>,

    pub qs: Option<Cp2kQsSettings>,
    pub mgrid: Option<Cp2kMgridSettings>,
    pub scf: Option<Cp2kScfSettings>,
    pub xc: Option<Cp2kXcSettings>,

    /// DFT-level settings that are often needed but not yet modelled.
    ///
    /// This is a structured escape hatch that can be extended later to a proper
    /// typed "overlay" model if needed.
    pub extra_keywords: Vec<Cp2kKeyword>,
    /// Extra subsections to be appended under `&DFT`.
    pub extra_sections: Vec<Cp2kSection>,
}

/// Settings for `&QS`.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kQsSettings {
    pub eps_default: Option<f64>,
}

/// Settings for `&MGRID`.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kMgridSettings {
    pub cutoff: Option<f64>,
    pub rel_cutoff: Option<f64>,
}

/// Settings for `&SCF`.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kScfSettings {
    pub scf_guess: Option<String>,
    pub max_scf: Option<i32>,
    pub eps_scf: Option<f64>,
    pub added_mos: Option<i32>,

    pub smear: Option<Cp2kSmearSettings>,
    pub diagonalization: Option<Cp2kDiagonalizationSettings>,
    pub mixing: Option<Cp2kMixingSettings>,

    pub extra_keywords: Vec<Cp2kKeyword>,
    pub extra_sections: Vec<Cp2kSection>,
}

/// Settings for `&SMEAR`.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kSmearSettings {
    pub enabled: bool,
    pub method: Option<String>,
    pub electronic_temperature_k: Option<f64>,
}

/// Settings for `&DIAGONALIZATION`.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kDiagonalizationSettings {
    pub enabled: bool,
    pub algorithm: Option<String>,
}

/// Settings for `&MIXING`.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kMixingSettings {
    pub method: Option<String>,
    pub alpha: Option<f64>,
    pub nbroyden: Option<i32>,
}

/// Settings for `&XC`.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kXcSettings {
    /// E.g. "PBE"
    pub functional: Option<String>,

    pub extra_keywords: Vec<Cp2kKeyword>,
    pub extra_sections: Vec<Cp2kSection>,
}

/// A generic keyword representation for future structured extensions.
///
/// This is not intended to be the primary user-facing interface; it provides a
/// controlled escape hatch while keeping `Cp2kSettings` the source of truth.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Cp2kKeyword {
    pub name: String,
    pub value: String,
    /// Optional unit token, e.g. "K" or "Ry". The renderer is responsible for
    /// formatting it as `NAME [unit] value` where appropriate.
    pub unit: Option<String>,
}

/// A generic section representation for future structured extensions.
///
/// This is a controlled escape hatch; note that CP2K allows repeated sections
/// and section parameters (`&KIND Si`).
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Cp2kSection {
    /// Section name, e.g. "KIND" or "PRINT".
    pub name: String,
    /// Optional section parameter, e.g. `Si` in `&KIND Si`.
    pub parameter: Option<String>,
    pub keywords: Vec<Cp2kKeyword>,
    pub sections: Vec<Cp2kSection>,
}

// ─── K-Point configuration types ─────────────────────────────────────────────

/// Specifies how k-points are generated
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum KPointScheme {
    /// No k-points (isolated molecule)
    None,
    /// Gamma point only
    Gamma,
    /// Monkhorst-Pack grid with specified divisions in each direction
    /// (n1, n2, n3)
    MonkhorstPack(u32, u32, u32),
    /// Explicit list of k-points: each entry is [kx, ky, kz, weight]
    ExplicitList(Vec<[f64; 4]>),
}

/// Configuration for k-points and related settings
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KPointConfig {
    /// The k-point scheme
    pub scheme: KPointScheme,
    /// Whether to use symmetry reduction of k-points
    pub use_symmetry: bool,
    /// Verbosity for k-point information
    pub verbose: bool,
    /// Whether to use full grid (no symmetry reduction)
    pub full_grid: bool,
}

/// Configuration for DOS calculations
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DosConfig {
    /// Whether to compute electronic density of states
    pub enabled: bool,
    /// Whether to compute partial DOS (per atom/orbital)
    pub partial_dos: bool,
    /// Energy range (min, max) in eV
    pub erange_ev: Option<(f64, f64)>,
    /// Number of energy points for DOS grid
    pub npoints: Option<usize>,
}

/// A command sent from the Python frontend to the worker.
#[derive(Debug, Serialize, Deserialize)]
#[allow(clippy::large_enum_variant)]
pub enum Command {
    /// Initialize a new force environment.
    ///
    /// This is the legacy entry point that takes a path to a `.inp` file.
    /// Prefer [`InitForceEnvWithGeometry`], which supports geometry injection.
    InitForceEnv { input: String, output: String },

    /// Initialise a new force environment and supply the starting geometry.
    ///
    /// The worker constructs a temporary input by taking the input specified by
    /// `input_spec` and injecting `&CELL` and `&COORD` sections into `&SUBSYS`
    /// before passing the (temporary) file to CP2K.
    ///
    /// For `input_spec = FromFile`, the file is read and patched.
    /// For `input_spec = Generated`, the settings are rendered and then patched
    /// (patching is still used to guarantee geometry correctness).
    ///
    /// Fields
    /// ------
    /// input_spec      Input source (file or generated settings).
    /// output          Path for CP2K output.
    /// symbols         Element symbol for each atom, e.g. `["W", "Al", "O"]`.
    /// positions_angstrom Flat interleaved array [x0,y0,z0, x1,y1,z1, …] in Å.
    /// cell_angstrom       9-element row-major cell matrix in Å.
    ///                     cell_angstrom[i*3 + j] = j-th component of i-th lattice vector.
    /// periodic            Periodicity string passed to CP2K's PERIODIC keyword.
    ///                     Use `"XYZ"` for fully periodic systems (the common case),
    ///                     `"NONE"` for isolated molecules. Any valid CP2K PERIODIC
    ///                     token is accepted.
    InitForceEnvWithGeometry {
        input: Cp2kInputSpec,
        output: String,
        symbols: Vec<String>,
        positions_angstrom: Vec<f64>,
        cell_angstrom: Vec<f64>,
        periodic: String,
        /// Optional project label for the &GLOBAL block
        project_label: Option<String>,
        /// Optional print level for the &GLOBAL block
        print_level: Option<String>,
        /// Optional RUN_TYPE for the &GLOBAL block
        run_type: Option<String>,
        /// Optional CHARGE for the &DFT block
        charge: Option<i32>,
        /// Optional MULTIPLICITY for the &DFT block
        multiplicity: Option<i32>,
        /// Optional WALLTIME for the &GLOBAL block
        walltime: Option<String>,
        /// Optional SEED for the &GLOBAL block
        seed: Option<Vec<i32>>,
        /// Optional ECHO_INPUT for the &GLOBAL block
        echo_input: Option<bool>,
        /// Optional UKS (Unrestricted Kohn-Sham) for the &DFT block
        uks: Option<bool>,
        /// Optional ROKS (Restricted Open Kohn-Sham) for the &DFT block
        roks: Option<bool>,
        /// Optional WFN_RESTART_FILE_NAME for the &DFT block
        wfn_restart_file: Option<String>,
        /// Optional k-point configuration for periodic systems
        kpoint_config: Option<KPointConfig>,
        /// Optional DOS configuration
        dos_config: Option<DosConfig>,
    },
    /// Calculate energy and forces for the current geometry.
    CalcEnergyForce,
    /// Calculate energy only.
    CalcEnergy,
    /// Query the number of atoms.
    GetNatom,
    /// Query the number of particles.
    GetNparticle,
    /// Retrieve particle positions as a flat f64 array.
    GetPositions,
    /// Retrieve forces as a flat f64 array.
    GetForces,
    /// Retrieve the potential energy.
    GetPotentialEnergy,
    /// Retrieve the 3x3 simulation cell (row-major flat f64).
    GetCell,
    /// Retrieve the 3x3 QMMM cell.
    GetQmmmCell,
    /// Set particle positions.
    SetPositions { data: Vec<f64> },
    /// Set particle velocities.
    SetVelocities { data: Vec<f64> },
    /// Set simulation cell (9 floats, row-major).
    SetCell { data: Vec<f64> },
    /// Get number of MOs in the active space.
    GetMoCount,
    // ----- extended interface -----
    #[cfg(feature = "extended")]
    IsQuickstep,
    #[cfg(feature = "extended")]
    GetStressTensor,
    #[cfg(feature = "extended")]
    GetVirialTensor,
    #[cfg(feature = "extended")]
    GetNmo { spin: i32 },
    #[cfg(feature = "extended")]
    GetEigenvalues { spin: i32 },
    #[cfg(feature = "extended")]
    GetOccupationNumbers { spin: i32 },
    #[cfg(feature = "extended")]
    GetHomoLumo { spin: i32 },
    #[cfg(feature = "extended")]
    GetMullikenCharges,
    #[cfg(feature = "extended")]
    GetHirshfeldCharges,
    #[cfg(feature = "extended")]
    GetDipoleMoment,
    #[cfg(feature = "extended")]
    GetScfInfo,
    #[cfg(feature = "extended")]
    GetEnergyComponents,
    #[cfg(feature = "extended")]
    GetNelectron,
    #[cfg(feature = "extended")]
    GetFermiEnergy,
    #[cfg(feature = "extended")]
    GetTotalSpin,
    #[cfg(feature = "extended")]
    GetGridInfo { spin: i32 },
    #[cfg(feature = "extended")]
    GetElectronDensity { spin: i32 },
    #[cfg(feature = "extended")]
    GetMoCoeffInfo { spin: i32 },
    #[cfg(feature = "extended")]
    GetMoCoefficients { spin: i32 },
    /// Get the number of k-points (0 = Gamma-point-only).
    #[cfg(feature = "extended")]
    GetNkpoints,
    /// Get Kohn-Sham eigenvalues for one k-point and one spin channel.
    #[cfg(feature = "extended")]
    GetKpointEigenvalues { kpt_idx: i32, spin: i32 },
    /// Ask the worker to shut down cleanly.
    Shutdown,
    /// Get CP2K version string.
    GetVersion,
}

/// A request wrapper carrying a unique ID and a command.
#[derive(Debug, Serialize, Deserialize)]
pub struct Request {
    pub request_id: u64,
    pub command: Command,
}

/// Status of a response.
#[derive(Debug, Serialize, Deserialize)]
pub enum Status {
    Ok,
    Error(String),
}

/// The payload of a successful response.
#[derive(Debug, Serialize, Deserialize)]
pub enum Payload {
    /// No data to return (void operations).
    Empty,
    /// A single integer value.
    Int(i64),
    /// A single unsigned integer value.
    UInt(u64),
    /// A single float value.
    Float(f64),
    /// A boolean value.
    Bool(bool),
    /// A 1-D f64 array (flat).
    Array1(Vec<f64>),
    /// A 2-D f64 array (shape[0], shape[1], flat row-major data).
    Array2 {
        rows: usize,
        cols: usize,
        data: Vec<f64>,
    },
    /// A string value.
    String(String),
    /// Four values: two floats and two ints (HOMO/LUMO).
    HomoLumo {
        homo: f64,
        lumo: f64,
        homo_idx: i32,
        lumo_idx: i32,
    },
    /// Five floats (energy components).
    EnergyComponents {
        e_kin: f64,
        e_hartree: f64,
        e_xc: f64,
        e_core: f64,
        e_total: f64,
    },
    /// SCF info: steps, converged, energy_change.
    ScfInfo {
        nsteps: i32,
        converged: bool,
        energy_change: f64,
    },
    /// Grid metadata for the electron density.
    GridInfo {
        npts: [i32; 3],
        origin: [f64; 3],
        dh: [[f64; 3]; 3],
    },
    /// Large array data stored in POSIX shared memory (3D).
    SharedArray3 {
        shm_name: String,
        dims: [usize; 3],
        byte_size: usize,
    },
    /// Large array data stored in POSIX shared memory (2D).
    SharedArray2 {
        shm_name: String,
        rows: usize,
        cols: usize,
        byte_size: usize,
    },
    /// MO coefficient dimensions.
    MoCoeffInfo { nao: usize, nmo: usize },
}

/// A response from the worker back to the Python frontend.
#[derive(Debug, Serialize, Deserialize)]
pub struct Response {
    pub request_id: u64,
    pub status: Status,
    pub payload: Payload,
}

impl Response {
    pub fn ok(request_id: u64, payload: Payload) -> Self {
        Response {
            request_id,
            status: Status::Ok,
            payload,
        }
    }

    pub fn error(request_id: u64, msg: impl Into<String>) -> Self {
        Response {
            request_id,
            status: Status::Error(msg.into()),
            payload: Payload::Empty,
        }
    }
}