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}