Skip to main content

cp2k_rs/
worker_protocol.rs

1//! IPC protocol types for the CP2K worker process.
2//!
3//! Serialization uses bincode for compact binary encoding.
4//! All messages are length-prefixed: [u32 length][payload bytes].
5
6use serde::{Deserialize, Serialize};
7
8// K-Point configuration types
9// ─── CP2K input specification ────────────────────────────────────────────────
10
11/// How the CP2K worker should obtain the `.inp` file.
12///
13/// This is the "source of truth" input mechanism: either the user supplies a
14/// path (`FromFile`) or a typed settings model is rendered into an input file
15/// (`Generated`).
16///
17/// Note: The worker ultimately needs a file path for CP2K. For `Generated`,
18/// the worker is expected to render settings to a temporary `.inp` file.
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[allow(clippy::large_enum_variant)]
21pub enum Cp2kInputSpec {
22    /// Use an existing CP2K input file on disk.
23    FromFile { path: String },
24    /// Generate an input file from typed settings.
25    Generated { settings: Cp2kSettings },
26}
27
28/// A minimal, typed CP2K settings model.
29///
30/// This is intentionally scoped to the most common Quickstep/DFT parameters we
31/// want to support without requiring a handwritten `.inp` file.
32///
33/// You can always extend this model over time.
34#[derive(Debug, Clone, Serialize, Deserialize, Default)]
35pub struct Cp2kSettings {
36    /// Settings for the `&GLOBAL` section.
37    pub global: Option<Cp2kGlobalSettings>,
38    /// Settings for `&FORCE_EVAL` (only the common `METHOD Quickstep` path is
39    /// addressed initially).
40    pub force_eval: Cp2kForceEvalSettings,
41}
42
43/// Settings for `&GLOBAL`.
44#[derive(Debug, Clone, Serialize, Deserialize, Default)]
45pub struct Cp2kGlobalSettings {
46    /// `PROJECT` label.
47    pub project: Option<String>,
48    /// `PRINT_LEVEL`
49    pub print_level: Option<String>,
50    /// `RUN_TYPE`
51    pub run_type: Option<String>,
52    /// `WALLTIME` (CP2K string, e.g. "00:59:00")
53    pub walltime: Option<String>,
54    /// `SEED` values. CP2K accepts either one integer or multiple integers.
55    pub seed: Option<Vec<i32>>,
56    /// `ECHO_INPUT`
57    pub echo_input: Option<bool>,
58}
59
60/// Settings for a `&FORCE_EVAL` section, focusing on Quickstep/DFT.
61///
62/// `&SUBSYS` geometry (`&CELL` and `&COORD`) is always injected from the frontend
63/// geometry and is therefore not part of this settings struct.
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct Cp2kForceEvalSettings {
66    /// `METHOD` (commonly "Quickstep").
67    pub method: String,
68    /// Optional stress tensor request (e.g. "ANALYTICAL").
69    pub stress_tensor: Option<String>,
70    /// DFT settings (for `METHOD Quickstep`).
71    pub dft: Option<Cp2kDftSettings>,
72    /// Subsystem settings (e.g. `&KIND` blocks).
73    pub subsys: Option<Cp2kSubsysSettings>,
74}
75
76impl Default for Cp2kForceEvalSettings {
77    fn default() -> Self {
78        Self {
79            method: "Quickstep".to_string(),
80            stress_tensor: None,
81            dft: Some(Cp2kDftSettings::default()),
82            subsys: None,
83        }
84    }
85}
86
87/// Settings for `&SUBSYS` (excluding geometry injection).
88#[derive(Debug, Clone, Serialize, Deserialize, Default)]
89pub struct Cp2kSubsysSettings {
90    /// `&KIND` blocks for element-specific basis/potential.
91    pub kinds: Vec<Cp2kKindSettings>,
92    /// Extra subsections to be appended under `&SUBSYS`.
93    pub extra_sections: Vec<Cp2kSection>,
94    /// Extra keywords to be appended under `&SUBSYS`.
95    pub extra_keywords: Vec<Cp2kKeyword>,
96}
97
98/// Settings for a `&KIND` block (e.g. `&KIND Si`).
99#[derive(Debug, Clone, Serialize, Deserialize, Default)]
100pub struct Cp2kKindSettings {
101    /// Element symbol, used as the `&KIND` parameter.
102    pub element: String,
103    /// `BASIS_SET` keyword.
104    pub basis_set: Option<String>,
105    /// `POTENTIAL` keyword.
106    pub potential: Option<String>,
107    /// Extra keywords inside the `&KIND` block.
108    pub extra_keywords: Vec<Cp2kKeyword>,
109    /// Extra subsections inside the `&KIND` block.
110    pub extra_sections: Vec<Cp2kSection>,
111}
112
113/// Settings for `&DFT` (Quickstep).
114#[derive(Debug, Clone, Serialize, Deserialize, Default)]
115pub struct Cp2kDftSettings {
116    pub basis_set_file_name: Option<String>,
117    pub potential_file_name: Option<String>,
118
119    pub qs: Option<Cp2kQsSettings>,
120    pub mgrid: Option<Cp2kMgridSettings>,
121    pub scf: Option<Cp2kScfSettings>,
122    pub xc: Option<Cp2kXcSettings>,
123
124    /// DFT-level settings that are often needed but not yet modelled.
125    ///
126    /// This is a structured escape hatch that can be extended later to a proper
127    /// typed "overlay" model if needed.
128    pub extra_keywords: Vec<Cp2kKeyword>,
129    /// Extra subsections to be appended under `&DFT`.
130    pub extra_sections: Vec<Cp2kSection>,
131}
132
133/// Settings for `&QS`.
134#[derive(Debug, Clone, Serialize, Deserialize, Default)]
135pub struct Cp2kQsSettings {
136    pub eps_default: Option<f64>,
137}
138
139/// Settings for `&MGRID`.
140#[derive(Debug, Clone, Serialize, Deserialize, Default)]
141pub struct Cp2kMgridSettings {
142    pub cutoff: Option<f64>,
143    pub rel_cutoff: Option<f64>,
144}
145
146/// Settings for `&SCF`.
147#[derive(Debug, Clone, Serialize, Deserialize, Default)]
148pub struct Cp2kScfSettings {
149    pub scf_guess: Option<String>,
150    pub max_scf: Option<i32>,
151    pub eps_scf: Option<f64>,
152    pub added_mos: Option<i32>,
153
154    pub smear: Option<Cp2kSmearSettings>,
155    pub diagonalization: Option<Cp2kDiagonalizationSettings>,
156    pub mixing: Option<Cp2kMixingSettings>,
157
158    pub extra_keywords: Vec<Cp2kKeyword>,
159    pub extra_sections: Vec<Cp2kSection>,
160}
161
162/// Settings for `&SMEAR`.
163#[derive(Debug, Clone, Serialize, Deserialize, Default)]
164pub struct Cp2kSmearSettings {
165    pub enabled: bool,
166    pub method: Option<String>,
167    pub electronic_temperature_k: Option<f64>,
168}
169
170/// Settings for `&DIAGONALIZATION`.
171#[derive(Debug, Clone, Serialize, Deserialize, Default)]
172pub struct Cp2kDiagonalizationSettings {
173    pub enabled: bool,
174    pub algorithm: Option<String>,
175}
176
177/// Settings for `&MIXING`.
178#[derive(Debug, Clone, Serialize, Deserialize, Default)]
179pub struct Cp2kMixingSettings {
180    pub method: Option<String>,
181    pub alpha: Option<f64>,
182    pub nbroyden: Option<i32>,
183}
184
185/// Settings for `&XC`.
186#[derive(Debug, Clone, Serialize, Deserialize, Default)]
187pub struct Cp2kXcSettings {
188    /// E.g. "PBE"
189    pub functional: Option<String>,
190
191    pub extra_keywords: Vec<Cp2kKeyword>,
192    pub extra_sections: Vec<Cp2kSection>,
193}
194
195/// A generic keyword representation for future structured extensions.
196///
197/// This is not intended to be the primary user-facing interface; it provides a
198/// controlled escape hatch while keeping `Cp2kSettings` the source of truth.
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct Cp2kKeyword {
201    pub name: String,
202    pub value: String,
203    /// Optional unit token, e.g. "K" or "Ry". The renderer is responsible for
204    /// formatting it as `NAME [unit] value` where appropriate.
205    pub unit: Option<String>,
206}
207
208/// A generic section representation for future structured extensions.
209///
210/// This is a controlled escape hatch; note that CP2K allows repeated sections
211/// and section parameters (`&KIND Si`).
212#[derive(Debug, Clone, Serialize, Deserialize, Default)]
213pub struct Cp2kSection {
214    /// Section name, e.g. "KIND" or "PRINT".
215    pub name: String,
216    /// Optional section parameter, e.g. `Si` in `&KIND Si`.
217    pub parameter: Option<String>,
218    pub keywords: Vec<Cp2kKeyword>,
219    pub sections: Vec<Cp2kSection>,
220}
221
222// ─── K-Point configuration types ─────────────────────────────────────────────
223
224/// Specifies how k-points are generated
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub enum KPointScheme {
227    /// No k-points (isolated molecule)
228    None,
229    /// Gamma point only
230    Gamma,
231    /// Monkhorst-Pack grid with specified divisions in each direction
232    /// (n1, n2, n3)
233    MonkhorstPack(u32, u32, u32),
234    /// Explicit list of k-points: each entry is [kx, ky, kz, weight]
235    ExplicitList(Vec<[f64; 4]>),
236}
237
238/// Configuration for k-points and related settings
239#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct KPointConfig {
241    /// The k-point scheme
242    pub scheme: KPointScheme,
243    /// Whether to use symmetry reduction of k-points
244    pub use_symmetry: bool,
245    /// Verbosity for k-point information
246    pub verbose: bool,
247    /// Whether to use full grid (no symmetry reduction)
248    pub full_grid: bool,
249}
250
251/// Configuration for DOS calculations
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct DosConfig {
254    /// Whether to compute electronic density of states
255    pub enabled: bool,
256    /// Whether to compute partial DOS (per atom/orbital)
257    pub partial_dos: bool,
258    /// Energy range (min, max) in eV
259    pub erange_ev: Option<(f64, f64)>,
260    /// Number of energy points for DOS grid
261    pub npoints: Option<usize>,
262}
263
264/// A command sent from the Python frontend to the worker.
265#[derive(Debug, Serialize, Deserialize)]
266#[allow(clippy::large_enum_variant)]
267pub enum Command {
268    /// Initialize a new force environment.
269    ///
270    /// This is the legacy entry point that takes a path to a `.inp` file.
271    /// Prefer [`InitForceEnvWithGeometry`], which supports geometry injection.
272    InitForceEnv { input: String, output: String },
273
274    /// Initialise a new force environment and supply the starting geometry.
275    ///
276    /// The worker constructs a temporary input by taking the input specified by
277    /// `input_spec` and injecting `&CELL` and `&COORD` sections into `&SUBSYS`
278    /// before passing the (temporary) file to CP2K.
279    ///
280    /// For `input_spec = FromFile`, the file is read and patched.
281    /// For `input_spec = Generated`, the settings are rendered and then patched
282    /// (patching is still used to guarantee geometry correctness).
283    ///
284    /// Fields
285    /// ------
286    /// input_spec      Input source (file or generated settings).
287    /// output          Path for CP2K output.
288    /// symbols         Element symbol for each atom, e.g. `["W", "Al", "O"]`.
289    /// positions_angstrom Flat interleaved array [x0,y0,z0, x1,y1,z1, …] in Å.
290    /// cell_angstrom       9-element row-major cell matrix in Å.
291    ///                     cell_angstrom[i*3 + j] = j-th component of i-th lattice vector.
292    /// periodic            Periodicity string passed to CP2K's PERIODIC keyword.
293    ///                     Use `"XYZ"` for fully periodic systems (the common case),
294    ///                     `"NONE"` for isolated molecules. Any valid CP2K PERIODIC
295    ///                     token is accepted.
296    InitForceEnvWithGeometry {
297        input: Cp2kInputSpec,
298        output: String,
299        symbols: Vec<String>,
300        positions_angstrom: Vec<f64>,
301        cell_angstrom: Vec<f64>,
302        periodic: String,
303        /// Optional project label for the &GLOBAL block
304        project_label: Option<String>,
305        /// Optional print level for the &GLOBAL block
306        print_level: Option<String>,
307        /// Optional RUN_TYPE for the &GLOBAL block
308        run_type: Option<String>,
309        /// Optional CHARGE for the &DFT block
310        charge: Option<i32>,
311        /// Optional MULTIPLICITY for the &DFT block
312        multiplicity: Option<i32>,
313        /// Optional WALLTIME for the &GLOBAL block
314        walltime: Option<String>,
315        /// Optional SEED for the &GLOBAL block
316        seed: Option<Vec<i32>>,
317        /// Optional ECHO_INPUT for the &GLOBAL block
318        echo_input: Option<bool>,
319        /// Optional UKS (Unrestricted Kohn-Sham) for the &DFT block
320        uks: Option<bool>,
321        /// Optional ROKS (Restricted Open Kohn-Sham) for the &DFT block
322        roks: Option<bool>,
323        /// Optional WFN_RESTART_FILE_NAME for the &DFT block
324        wfn_restart_file: Option<String>,
325        /// Optional k-point configuration for periodic systems
326        kpoint_config: Option<KPointConfig>,
327        /// Optional DOS configuration
328        dos_config: Option<DosConfig>,
329    },
330    /// Calculate energy and forces for the current geometry.
331    CalcEnergyForce,
332    /// Calculate energy only.
333    CalcEnergy,
334    /// Query the number of atoms.
335    GetNatom,
336    /// Query the number of particles.
337    GetNparticle,
338    /// Retrieve particle positions as a flat f64 array.
339    GetPositions,
340    /// Retrieve forces as a flat f64 array.
341    GetForces,
342    /// Retrieve the potential energy.
343    GetPotentialEnergy,
344    /// Retrieve the 3x3 simulation cell (row-major flat f64).
345    GetCell,
346    /// Retrieve the 3x3 QMMM cell.
347    GetQmmmCell,
348    /// Set particle positions.
349    SetPositions { data: Vec<f64> },
350    /// Set particle velocities.
351    SetVelocities { data: Vec<f64> },
352    /// Set simulation cell (9 floats, row-major).
353    SetCell { data: Vec<f64> },
354    /// Get number of MOs in the active space.
355    GetMoCount,
356    // ----- extended interface -----
357    #[cfg(feature = "extended")]
358    IsQuickstep,
359    #[cfg(feature = "extended")]
360    GetStressTensor,
361    #[cfg(feature = "extended")]
362    GetVirialTensor,
363    #[cfg(feature = "extended")]
364    GetNmo { spin: i32 },
365    #[cfg(feature = "extended")]
366    GetEigenvalues { spin: i32 },
367    #[cfg(feature = "extended")]
368    GetOccupationNumbers { spin: i32 },
369    #[cfg(feature = "extended")]
370    GetHomoLumo { spin: i32 },
371    #[cfg(feature = "extended")]
372    GetMullikenCharges,
373    #[cfg(feature = "extended")]
374    GetHirshfeldCharges,
375    #[cfg(feature = "extended")]
376    GetDipoleMoment,
377    #[cfg(feature = "extended")]
378    GetScfInfo,
379    #[cfg(feature = "extended")]
380    GetEnergyComponents,
381    #[cfg(feature = "extended")]
382    GetNelectron,
383    #[cfg(feature = "extended")]
384    GetFermiEnergy,
385    #[cfg(feature = "extended")]
386    GetTotalSpin,
387    #[cfg(feature = "extended")]
388    GetGridInfo { spin: i32 },
389    #[cfg(feature = "extended")]
390    GetElectronDensity { spin: i32 },
391    #[cfg(feature = "extended")]
392    GetMoCoeffInfo { spin: i32 },
393    #[cfg(feature = "extended")]
394    GetMoCoefficients { spin: i32 },
395    /// Get the number of k-points (0 = Gamma-point-only).
396    #[cfg(feature = "extended")]
397    GetNkpoints,
398    /// Get Kohn-Sham eigenvalues for one k-point and one spin channel.
399    #[cfg(feature = "extended")]
400    GetKpointEigenvalues { kpt_idx: i32, spin: i32 },
401    /// Ask the worker to shut down cleanly.
402    Shutdown,
403    /// Get CP2K version string.
404    GetVersion,
405}
406
407/// A request wrapper carrying a unique ID and a command.
408#[derive(Debug, Serialize, Deserialize)]
409pub struct Request {
410    pub request_id: u64,
411    pub command: Command,
412}
413
414/// Status of a response.
415#[derive(Debug, Serialize, Deserialize)]
416pub enum Status {
417    Ok,
418    Error(String),
419}
420
421/// The payload of a successful response.
422#[derive(Debug, Serialize, Deserialize)]
423pub enum Payload {
424    /// No data to return (void operations).
425    Empty,
426    /// A single integer value.
427    Int(i64),
428    /// A single unsigned integer value.
429    UInt(u64),
430    /// A single float value.
431    Float(f64),
432    /// A boolean value.
433    Bool(bool),
434    /// A 1-D f64 array (flat).
435    Array1(Vec<f64>),
436    /// A 2-D f64 array (shape[0], shape[1], flat row-major data).
437    Array2 {
438        rows: usize,
439        cols: usize,
440        data: Vec<f64>,
441    },
442    /// A string value.
443    String(String),
444    /// Four values: two floats and two ints (HOMO/LUMO).
445    HomoLumo {
446        homo: f64,
447        lumo: f64,
448        homo_idx: i32,
449        lumo_idx: i32,
450    },
451    /// Five floats (energy components).
452    EnergyComponents {
453        e_kin: f64,
454        e_hartree: f64,
455        e_xc: f64,
456        e_core: f64,
457        e_total: f64,
458    },
459    /// SCF info: steps, converged, energy_change.
460    ScfInfo {
461        nsteps: i32,
462        converged: bool,
463        energy_change: f64,
464    },
465    /// Grid metadata for the electron density.
466    GridInfo {
467        npts: [i32; 3],
468        origin: [f64; 3],
469        dh: [[f64; 3]; 3],
470    },
471    /// Large array data stored in POSIX shared memory (3D).
472    SharedArray3 {
473        shm_name: String,
474        dims: [usize; 3],
475        byte_size: usize,
476    },
477    /// Large array data stored in POSIX shared memory (2D).
478    SharedArray2 {
479        shm_name: String,
480        rows: usize,
481        cols: usize,
482        byte_size: usize,
483    },
484    /// MO coefficient dimensions.
485    MoCoeffInfo { nao: usize, nmo: usize },
486}
487
488/// A response from the worker back to the Python frontend.
489#[derive(Debug, Serialize, Deserialize)]
490pub struct Response {
491    pub request_id: u64,
492    pub status: Status,
493    pub payload: Payload,
494}
495
496impl Response {
497    pub fn ok(request_id: u64, payload: Payload) -> Self {
498        Response {
499            request_id,
500            status: Status::Ok,
501            payload,
502        }
503    }
504
505    pub fn error(request_id: u64, msg: impl Into<String>) -> Self {
506        Response {
507            request_id,
508            status: Status::Error(msg.into()),
509            payload: Payload::Empty,
510        }
511    }
512}