Skip to content

Result Parser (result.py)

The core engine for building and running Multiwfn sequences.

pymultiwfn.analysis.result

Result container for Multiwfn job execution.

Provides the parsed-result dataclasses, the :class:MultiwfnResult container that accumulates parser output, and per-molecule JSON persistence (replacing the former storage.py).

Aromaticity dataclass

Bases: ParsedMultiwfnResult

Result for aromaticity analysis (Menu 25).

Source code in src/pymultiwfn/analysis/result.py
474
475
476
477
478
479
480
481
482
483
484
485
486
@dataclass
class Aromaticity(ParsedMultiwfnResult):
    """Result for aromaticity analysis (Menu 25)."""

    NICS: float | None = None
    NICS_ZZ: float | None = None
    NICS_1: float | None = None
    HOMA: float | None = None
    HOMAC: float | None = None
    HOMER: float | None = None
    Bird: float | None = None
    EN_GEO: float | None = None
    EN_BLA: float | None = None

AromaticityIndex dataclass

Bases: ParsedMultiwfnResult

Result for aromaticity indices (Menu 15).

Source code in src/pymultiwfn/analysis/result.py
275
276
277
278
279
280
@dataclass
class AromaticityIndex(ParsedMultiwfnResult):
    """Result for aromaticity indices (Menu 15)."""

    index_name: str
    value: float

BLA_BOA dataclass

Bases: ParsedMultiwfnResult

Result for BLA/BOA.

Source code in src/pymultiwfn/analysis/result.py
598
599
600
601
602
603
@dataclass
class BLA_BOA(ParsedMultiwfnResult):  # noqa: N801
    """Result for BLA/BOA."""

    bla: float | None = None
    boa: float | None = None

Basin dataclass

Bases: ParsedMultiwfnResult

Result for basin analysis.

Source code in src/pymultiwfn/analysis/result.py
286
287
288
289
290
291
292
293
294
295
@dataclass
class Basin(ParsedMultiwfnResult):
    """Result for basin analysis."""

    basin_id: int
    population: float
    attractor_atom: int | None = None
    attractor_element: str | None = None
    volume: float | None = None
    charge: float | None = None

BondAngle dataclass

Bases: ParsedMultiwfnResult

Result for bond angle analysis.

Source code in src/pymultiwfn/analysis/result.py
541
542
543
544
545
546
547
548
@dataclass
class BondAngle(ParsedMultiwfnResult):
    """Result for bond angle analysis."""

    atom1_id: int
    atom2_id: int
    atom3_id: int
    angle: float

BondLength dataclass

Bases: ParsedMultiwfnResult

Result for bond length analysis.

Source code in src/pymultiwfn/analysis/result.py
532
533
534
535
536
537
538
@dataclass
class BondLength(ParsedMultiwfnResult):
    """Result for bond length analysis."""

    atom1_id: int
    atom2_id: int
    length: float

BondOrder dataclass

Bases: ParsedMultiwfnResult

Result for bond order analysis.

Source code in src/pymultiwfn/analysis/result.py
100
101
102
103
104
105
106
@dataclass
class BondOrder(ParsedMultiwfnResult):
    """Result for bond order analysis."""

    atom1_id: int
    atom2_id: int
    bond_order: float

BondOrderDecomposition dataclass

Bases: ParsedMultiwfnResult

Result for bond order decomposition (per orbital) analysis.

Source code in src/pymultiwfn/analysis/result.py
109
110
111
112
113
114
@dataclass
class BondOrderDecomposition(ParsedMultiwfnResult):
    """Result for bond order decomposition (per orbital) analysis."""

    orbital_id: int
    contribution: float

BondPath dataclass

Bases: ParsedMultiwfnResult

Result for bond path analysis.

Source code in src/pymultiwfn/analysis/result.py
151
152
153
154
155
156
157
158
@dataclass
class BondPath(ParsedMultiwfnResult):
    """Result for bond path analysis."""

    atom1_id: int
    atom2_id: int
    bcp_id: int
    path_length: float

Charge dataclass

Bases: ParsedMultiwfnResult

Result for atomic charge analysis.

Source code in src/pymultiwfn/analysis/result.py
45
46
47
48
49
50
@dataclass
class Charge(ParsedMultiwfnResult):
    """Result for atomic charge analysis."""

    atom_id: int
    charge: float

ChargeTransfer dataclass

Bases: ParsedMultiwfnResult

Result for charge transfer analysis.

Source code in src/pymultiwfn/analysis/result.py
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
@dataclass
class ChargeTransfer(ParsedMultiwfnResult):
    """Result for charge transfer analysis."""

    distance: float | None = None
    transfer_amount: float | None = None
    fragments: list[ChargeTransferFragment] | None = None

    def to_dict(self) -> dict[str, Any]:
        d: dict[str, Any] = {
            "distance": self.distance,
            "transfer_amount": self.transfer_amount,
        }
        if self.fragments is not None:
            d["fragments"] = [asdict(f) for f in self.fragments]
        else:
            d["fragments"] = None
        return d

ChargeTransferFragment dataclass

Bases: ParsedMultiwfnResult

Individual fragment contribution to charge transfer analysis.

Source code in src/pymultiwfn/analysis/result.py
316
317
318
319
320
321
322
@dataclass
class ChargeTransferFragment(ParsedMultiwfnResult):
    """Individual fragment contribution to charge transfer analysis."""

    fragment_id: int
    hole_contribution: float
    electron_contribution: float

Color dataclass

Bases: ParsedMultiwfnResult

Result for color prediction in CIE XYZ color space and RGB values.

Source code in src/pymultiwfn/analysis/result.py
207
208
209
210
211
212
213
214
215
216
@dataclass
class Color(ParsedMultiwfnResult):
    """Result for color prediction in CIE XYZ color space and RGB values."""

    X: float
    Y: float  # noqa: E741
    Z: float
    R: int
    G: int
    B: int

CondensedFukui dataclass

Bases: ParsedMultiwfnResult

Result for condensed Fukui functions.

Source code in src/pymultiwfn/analysis/result.py
414
415
416
417
418
419
420
421
@dataclass
class CondensedFukui(ParsedMultiwfnResult):
    """Result for condensed Fukui functions."""

    atom_id: int
    fukui_plus: float | None = None
    fukui_minus: float | None = None
    fukui_zero: float | None = None

CoordinationNumber dataclass

Bases: ParsedMultiwfnResult

Result for coordination numbers.

Source code in src/pymultiwfn/analysis/result.py
590
591
592
593
594
595
@dataclass
class CoordinationNumber(ParsedMultiwfnResult):
    """Result for coordination numbers."""

    atom_id: int
    coordination_number: float

CriticalPoint dataclass

Bases: ParsedMultiwfnResult

Individual critical point.

Source code in src/pymultiwfn/analysis/result.py
137
138
139
140
141
142
143
144
145
146
147
148
@dataclass
class CriticalPoint(ParsedMultiwfnResult):
    """Individual critical point."""

    index: int
    x: float
    y: float
    z: float
    rho: float | None = None
    laplacian: float | None = None
    ellipticity: float | None = None
    type: Literal["nuclear", "bond", "ring", "cage", "unknown"] = "unknown"

Cube dataclass

Bases: ParsedMultiwfnResult

Result for cube operations.

Source code in src/pymultiwfn/analysis/result.py
514
515
516
517
518
519
520
521
522
523
524
525
526
@dataclass
class Cube(ParsedMultiwfnResult):
    """Result for cube operations."""

    file_name: str = ""
    x_dim: int = 0
    y_dim: int = 0
    z_dim: int = 0
    min: float | None = None
    max: float | None = None
    mean: float | None = None
    integral: float | None = None
    std_dev: float | None = None

DelocalizationIndex dataclass

Bases: ParsedMultiwfnResult

Result for delocalization index.

Source code in src/pymultiwfn/analysis/result.py
266
267
268
269
270
271
272
@dataclass
class DelocalizationIndex(ParsedMultiwfnResult):
    """Result for delocalization index."""

    atom1_id: int
    atom2_id: int
    index: float

DeltaR dataclass

Bases: ParsedMultiwfnResult

Result for Delta_r index.

Source code in src/pymultiwfn/analysis/result.py
345
346
347
348
349
350
@dataclass
class DeltaR(ParsedMultiwfnResult):
    """Result for Delta_r index."""

    state_id: int
    delta_r: float

DensityOfStates dataclass

Bases: ParsedMultiwfnResult

Result for density of states analysis.

Source code in src/pymultiwfn/analysis/result.py
164
165
166
167
168
169
170
@dataclass
class DensityOfStates(ParsedMultiwfnResult):
    """Result for density of states analysis."""

    energies_eV: list[float] = field(default_factory=list)  # noqa: N815
    dos: list[float] = field(default_factory=list)
    projected_dos: dict[str, list[float]] | None = None

DihedralAngle dataclass

Bases: ParsedMultiwfnResult

Result for dihedral angle analysis.

Source code in src/pymultiwfn/analysis/result.py
551
552
553
554
555
556
557
558
559
@dataclass
class DihedralAngle(ParsedMultiwfnResult):
    """Result for dihedral angle analysis."""

    atom1_id: int
    atom2_id: int
    atom3_id: int
    atom4_id: int
    angle: float

Dipole dataclass

Bases: ParsedMultiwfnResult

Result for dipole moment analysis.

Source code in src/pymultiwfn/analysis/result.py
53
54
55
56
57
58
59
60
@dataclass
class Dipole(ParsedMultiwfnResult):
    """Result for dipole moment analysis."""

    x: float
    y: float
    z: float
    total: float

DipoleMoment dataclass

Bases: ParsedMultiwfnResult

Result for dipole moment analysis.

Source code in src/pymultiwfn/analysis/result.py
562
563
564
565
566
567
568
@dataclass
class DipoleMoment(ParsedMultiwfnResult):
    """Result for dipole moment analysis."""

    x: float
    y: float
    z: float

DispersionContribution dataclass

Bases: ParsedMultiwfnResult

Result for dispersion contributions.

Source code in src/pymultiwfn/analysis/result.py
390
391
392
393
394
395
@dataclass
class DispersionContribution(ParsedMultiwfnResult):
    """Result for dispersion contributions."""

    atom_id: int
    contribution: float

DualDescriptor dataclass

Bases: ParsedMultiwfnResult

Result for dual descriptor.

Source code in src/pymultiwfn/analysis/result.py
424
425
426
427
428
429
@dataclass
class DualDescriptor(ParsedMultiwfnResult):
    """Result for dual descriptor."""

    atom_id: int
    value: float

EnergyDecompositionAnalysis dataclass

Bases: ParsedMultiwfnResult

Result for energy decomposition analysis.

Source code in src/pymultiwfn/analysis/result.py
377
378
379
380
381
382
383
384
385
386
387
@dataclass
class EnergyDecompositionAnalysis(ParsedMultiwfnResult):
    """Result for energy decomposition analysis."""

    electrostatic: float | None = None
    exchange: float | None = None
    repulsion: float | None = None
    polarization: float | None = None
    dispersion: float | None = None
    orbital_interaction: float | None = None
    total_interaction: float | None = None

FuzzyAtomicProperty dataclass

Bases: ParsedMultiwfnResult

Result for atomic properties in fuzzy space.

Source code in src/pymultiwfn/analysis/result.py
253
254
255
256
257
258
259
260
261
262
263
@dataclass
class FuzzyAtomicProperty(ParsedMultiwfnResult):
    """Result for atomic properties in fuzzy space."""

    atom_id: int
    population: float | None = None
    dipole_x: float | None = None
    dipole_y: float | None = None
    dipole_z: float | None = None
    quadrupole: float | None = None
    volume: float | None = None

HoleElectron dataclass

Bases: ParsedMultiwfnResult

Result for hole-electron analysis.

Source code in src/pymultiwfn/analysis/result.py
301
302
303
304
305
306
307
308
309
310
311
312
313
@dataclass
class HoleElectron(ParsedMultiwfnResult):
    """Result for hole-electron analysis."""

    hole_id: float
    electron_id: float
    transition_index: float
    electron_delocalisation_index: float
    hole_delocalisation_index: float
    Sr: float
    d_index: float
    hole_centroid: tuple[float, float, float] = (0.0, 0.0, 0.0)
    electron_centroid: tuple[float, float, float] = (0.0, 0.0, 0.0)

LambdaIndex dataclass

Bases: ParsedMultiwfnResult

Result for Lambda index.

Source code in src/pymultiwfn/analysis/result.py
353
354
355
356
357
358
@dataclass
class LambdaIndex(ParsedMultiwfnResult):
    """Result for Lambda index."""

    state_id: int
    lambda_index: float

LocalizationIndex dataclass

Bases: ParsedMultiwfnResult

Result for localization index.

Source code in src/pymultiwfn/analysis/result.py
606
607
608
609
610
611
@dataclass
class LocalizationIndex(ParsedMultiwfnResult):
    """Result for localization index."""

    atom_id: int
    index: float

MultiCenterBondOrder dataclass

Bases: ParsedMultiwfnResult

Result for multi-center bond order analysis.

Source code in src/pymultiwfn/analysis/result.py
126
127
128
129
130
131
@dataclass
class MultiCenterBondOrder(ParsedMultiwfnResult):
    """Result for multi-center bond order analysis."""

    atom_ids: list[int] = field(default_factory=list)
    bond_order: float = 0.0

MultipoleMoments dataclass

Bases: ParsedMultiwfnResult

Result for multipole moments.

Source code in src/pymultiwfn/analysis/result.py
583
584
585
586
587
@dataclass
class MultipoleMoments(ParsedMultiwfnResult):
    """Result for multipole moments."""

    moments: dict[str, float] = field(default_factory=dict)

MultiwfnResult

Container for parsed Multiwfn analysis results.

Each instance is bound to a single :class:Menu analysis type. After :meth:parse is called with raw stdout, the parsed :class:ParsedMultiwfnResult objects are available in :attr:result.

Parameters

analysis The Menu enum member identifying this analysis.

Source code in src/pymultiwfn/analysis/result.py
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
class MultiwfnResult:
    """Container for parsed Multiwfn analysis results.

    Each instance is bound to a single :class:`Menu` analysis type.
    After :meth:`parse` is called with raw stdout, the parsed
    :class:`ParsedMultiwfnResult` objects are available in
    :attr:`result`.

    Parameters
    ----------
    analysis
        The Menu enum member identifying this analysis.

    """

    def __init__(self, analysis: Menu) -> None:
        self.analysis: Menu = analysis
        self.result: list[ParsedMultiwfnResult] = []

    def parse(self, stdout: str) -> None:
        """Parse *stdout* using the router and store results.

        Imports :class:`ParserRoute` lazily to avoid circular imports
        (parsers.py imports dataclasses from this module).
        """
        from pymultiwfn.analysis.parsers import ParserRoute

        parser_cls = ParserRoute.ROUTE_TABLE.get(self.analysis)
        if parser_cls is None:
            return

        parsed = parser_cls.parse_for_result(self.analysis, stdout)
        if parsed:
            self.result.extend(parsed)

    # ── serialisation ────────────────────────────────────────────────────

    def to_dict(self) -> dict[str, Any]:
        """Serialise the result list to a JSON-safe dict."""
        return {
            "analysis": self.analysis.name,
            "results": [r.to_dict() for r in self.result],
        }

    def __repr__(self) -> str:
        return (
            f"MultiwfnResult(analysis={self.analysis.name!r}, "
            f"n_results={len(self.result)})"
        )

parse(stdout)

Parse stdout using the router and store results.

Imports :class:ParserRoute lazily to avoid circular imports (parsers.py imports dataclasses from this module).

Source code in src/pymultiwfn/analysis/result.py
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
def parse(self, stdout: str) -> None:
    """Parse *stdout* using the router and store results.

    Imports :class:`ParserRoute` lazily to avoid circular imports
    (parsers.py imports dataclasses from this module).
    """
    from pymultiwfn.analysis.parsers import ParserRoute

    parser_cls = ParserRoute.ROUTE_TABLE.get(self.analysis)
    if parser_cls is None:
        return

    parsed = parser_cls.parse_for_result(self.analysis, stdout)
    if parsed:
        self.result.extend(parsed)

to_dict()

Serialise the result list to a JSON-safe dict.

Source code in src/pymultiwfn/analysis/result.py
656
657
658
659
660
661
def to_dict(self) -> dict[str, Any]:
    """Serialise the result list to a JSON-safe dict."""
    return {
        "analysis": self.analysis.name,
        "results": [r.to_dict() for r in self.result],
    }

NICSScan dataclass

Bases: ParsedMultiwfnResult

Result for Nucleus Independent Chemical Shift scan.

Source code in src/pymultiwfn/analysis/result.py
489
490
491
492
493
494
@dataclass
class NICSScan(ParsedMultiwfnResult):
    """Result for Nucleus Independent Chemical Shift scan."""

    distances: list[float] = field(default_factory=list)
    values: list[float] = field(default_factory=list)

Orbital dataclass

Bases: ParsedMultiwfnResult

Result for orbital info.

Source code in src/pymultiwfn/analysis/result.py
500
501
502
503
504
505
506
507
508
@dataclass
class Orbital(ParsedMultiwfnResult):
    """Result for orbital info."""

    orbital_id: int
    energy_eV: float  # noqa: N815
    energy_au: float
    occupation: float
    spin: Literal["alpha", "beta"] | None = None

OrbitalComponent dataclass

Bases: ParsedMultiwfnResult

Result for orbital composition analysis.

Source code in src/pymultiwfn/analysis/result.py
74
75
76
77
78
79
80
81
82
83
84
85
86
@dataclass
class OrbitalComponent(ParsedMultiwfnResult):
    """Result for orbital composition analysis."""

    orbital_id: int
    occupation: float
    energy: float
    contributions: list[OrbitalContribution] = field(default_factory=list)

    def to_dict(self) -> dict[str, Any]:
        d = asdict(self)
        d["contributions"] = [asdict(c) for c in self.contributions]
        return d

OrbitalContribution dataclass

Individual contribution to an orbital.

Source code in src/pymultiwfn/analysis/result.py
66
67
68
69
70
71
@dataclass
class OrbitalContribution:
    """Individual contribution to an orbital."""

    label: str
    percentage: float

OrbitalEnergy dataclass

Bases: ParsedMultiwfnResult

Result for orbital energy analysis.

Source code in src/pymultiwfn/analysis/result.py
173
174
175
176
177
178
179
@dataclass
class OrbitalEnergy(ParsedMultiwfnResult):
    """Result for orbital energy analysis."""

    index: int
    energy_eV: float  # noqa: N815
    occupation: float

OxidationState dataclass

Bases: ParsedMultiwfnResult

Result for oxidation state analysis.

Source code in src/pymultiwfn/analysis/result.py
89
90
91
92
93
94
@dataclass
class OxidationState(ParsedMultiwfnResult):
    """Result for oxidation state analysis."""

    atom_id: int
    oxidation_state: int

ParsedMultiwfnResult dataclass

Base class for all Multiwfn parsed result types.

Source code in src/pymultiwfn/analysis/result.py
23
24
25
26
27
28
29
@dataclass
class ParsedMultiwfnResult:
    """Base class for all Multiwfn parsed result types."""

    def to_dict(self) -> dict[str, Any]:
        """Serialise to a plain dict (JSON-safe)."""
        return asdict(self)

to_dict()

Serialise to a plain dict (JSON-safe).

Source code in src/pymultiwfn/analysis/result.py
27
28
29
def to_dict(self) -> dict[str, Any]:
    """Serialise to a plain dict (JSON-safe)."""
    return asdict(self)

Polarizability dataclass

Bases: ParsedMultiwfnResult

Result for polarizability.

Source code in src/pymultiwfn/analysis/result.py
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
@dataclass
class Polarizability(ParsedMultiwfnResult):
    """Result for polarizability."""

    isotropic: float | None = None
    anisotropic: float | None = None
    beta_total: float | None = None
    gamma_total: float | None = None
    tensor: PolarizabilityTensor | None = None

    def to_dict(self) -> dict[str, Any]:
        d: dict[str, Any] = {
            "isotropic": self.isotropic,
            "anisotropic": self.anisotropic,
            "beta_total": self.beta_total,
            "gamma_total": self.gamma_total,
        }
        if self.tensor is not None:
            d["tensor"] = asdict(self.tensor)
        else:
            d["tensor"] = None
        return d

PolarizabilityTensor dataclass

Bases: ParsedMultiwfnResult

Result for polarizability tensor.

Source code in src/pymultiwfn/analysis/result.py
435
436
437
438
439
440
441
442
443
444
@dataclass
class PolarizabilityTensor(ParsedMultiwfnResult):
    """Result for polarizability tensor."""

    alpha_xx: float = 0.0
    alpha_xy: float = 0.0
    alpha_xz: float = 0.0
    alpha_yy: float = 0.0
    alpha_yz: float = 0.0
    alpha_zz: float = 0.0

QuadrupoleMoment dataclass

Bases: ParsedMultiwfnResult

Result for quadrupole moment analysis.

Source code in src/pymultiwfn/analysis/result.py
571
572
573
574
575
576
577
578
579
580
@dataclass
class QuadrupoleMoment(ParsedMultiwfnResult):
    """Result for quadrupole moment analysis."""

    xx: float | None = None
    xy: float | None = None
    xz: float | None = None
    yy: float | None = None
    yz: float | None = None
    zz: float | None = None

Reactivity dataclass

Bases: ParsedMultiwfnResult

Result for global reactivity indices.

Source code in src/pymultiwfn/analysis/result.py
401
402
403
404
405
406
407
408
409
410
411
@dataclass
class Reactivity(ParsedMultiwfnResult):
    """Result for global reactivity indices."""

    chemical_potential: float | None = None
    hardness: float | None = None
    softness: float | None = None
    electrophilicity: float | None = None
    nucleophilicity: float | None = None
    ionization_potential: float | None = None
    electron_affinity: float | None = None

ResultStore

Per-molecule result store with optional JSON persistence.

Parsed results are always cached in memory so that :meth:has_result and :meth:get_result work regardless of whether a JSON file exists on disk.

Parameters

input_file Path to the wavefunction input file. work_dir Directory used for the working data. Created if it does not exist. json_path Controls JSON persistence:

* ``None`` (the default) — results are cached in memory only;
  no file is written to disk.
* A :class:`~pathlib.Path` — results are written to that exact
  file every time :meth:`store` is called.  If the file already
  exists it is loaded on construction so that previous results
  are available immediately.

The value can be changed at any time via the :attr:`json_path`
property.  Setting it from ``None`` to a ``Path`` will
immediately flush the current in-memory data to disk.
Source code in src/pymultiwfn/analysis/result.py
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
class ResultStore:
    """Per-molecule result store with optional JSON persistence.

    Parsed results are always cached in memory so that
    :meth:`has_result` and :meth:`get_result` work regardless of
    whether a JSON file exists on disk.

    Parameters
    ----------
    input_file
        Path to the wavefunction input file.
    work_dir
        Directory used for the working data.  Created if it does not
        exist.
    json_path
        Controls JSON persistence:

        * ``None`` (the default) — results are cached in memory only;
          no file is written to disk.
        * A :class:`~pathlib.Path` — results are written to that exact
          file every time :meth:`store` is called.  If the file already
          exists it is loaded on construction so that previous results
          are available immediately.

        The value can be changed at any time via the :attr:`json_path`
        property.  Setting it from ``None`` to a ``Path`` will
        immediately flush the current in-memory data to disk.

    """

    def __init__(
        self,
        input_file: Path,
        work_dir: Path,
        json_path: Path | None = None,
    ) -> None:
        self._input_file = Path(input_file)
        self._work_dir = Path(work_dir)
        self._work_dir.mkdir(parents=True, exist_ok=True)

        self._json_path: Path | None = (
            Path(json_path) if json_path is not None else None
        )
        self._data: dict[str, Any] = self._load()

    @property
    def json_path(self) -> Path | None:
        """Path to the JSON file, or ``None`` if persistence is disabled."""
        return self._json_path

    @json_path.setter
    def json_path(self, value: Path | None) -> None:
        self._json_path = Path(value) if value is not None else None
        if self._json_path is not None:
            # Flush current in-memory data to disk immediately.
            self._save()

    @property
    def data(self) -> dict[str, Any]:
        """Return a *copy* of the stored data."""
        return dict(self._data)

    # ── persistence ──────────────────────────────────────────────────────

    def _load(self) -> dict[str, Any]:
        if self._json_path is not None and self._json_path.exists():
            with Path.open(self._json_path, encoding="utf-8") as f:
                return json.load(f)
        return {
            "input_file": str(self._input_file),
            "analyses": {},
        }

    def _save(self) -> None:
        if self._json_path is None:
            return
        self._json_path.parent.mkdir(parents=True, exist_ok=True)
        with Path.open(self._json_path, "w", encoding="utf-8") as f:
            json.dump(self._data, f, indent=2, default=str)

    # ── read / write ─────────────────────────────────────────────────────

    def has_result(self, analysis: Menu) -> bool:
        """Check whether a parsed result already exists for *analysis*."""
        return analysis.name in self._data.get("analyses", {})

    def get_result(self, analysis: Menu) -> dict[str, Any] | None:
        """Retrieve a previously stored parsed result, or ``None``."""
        return self._data.get("analyses", {}).get(analysis.name)

    def store(self, mwfn_result: MultiwfnResult) -> None:
        """Cache a :class:`MultiwfnResult` in memory.

        If :attr:`json_path` is not ``None``, the result is also
        written to the JSON file on disk.

        Parameters
        ----------
        mwfn_result
            A fully parsed ``MultiwfnResult`` whose ``.result`` list
            is non-empty.

        """
        if not mwfn_result.result:
            return

        entry: dict[str, Any] = {
            "parsed": mwfn_result.to_dict(),
            "timestamp": datetime.now().isoformat(),
        }
        self._data.setdefault("analyses", {})[mwfn_result.analysis.name] = (
            entry
        )
        self._save()

    def store_from_stdout(
        self,
        analysis: Menu,
        stdout: str,
    ) -> MultiwfnResult | None:
        """Parse *stdout*, persist, and return the :class:`MultiwfnResult`.

        Convenience method combining :meth:`MultiwfnResult.parse` and
        :meth:`store` in a single call.

        Returns ``None`` if no parser is available or parsing yields no
        results.
        """
        result = MultiwfnResult(analysis=analysis)
        result.parse(stdout)
        if not result.result:
            return None
        self.store(result)
        return result

data property

Return a copy of the stored data.

json_path property writable

Path to the JSON file, or None if persistence is disabled.

get_result(analysis)

Retrieve a previously stored parsed result, or None.

Source code in src/pymultiwfn/analysis/result.py
761
762
763
def get_result(self, analysis: Menu) -> dict[str, Any] | None:
    """Retrieve a previously stored parsed result, or ``None``."""
    return self._data.get("analyses", {}).get(analysis.name)

has_result(analysis)

Check whether a parsed result already exists for analysis.

Source code in src/pymultiwfn/analysis/result.py
757
758
759
def has_result(self, analysis: Menu) -> bool:
    """Check whether a parsed result already exists for *analysis*."""
    return analysis.name in self._data.get("analyses", {})

store(mwfn_result)

Cache a :class:MultiwfnResult in memory.

If :attr:json_path is not None, the result is also written to the JSON file on disk.

Parameters

mwfn_result A fully parsed MultiwfnResult whose .result list is non-empty.

Source code in src/pymultiwfn/analysis/result.py
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
def store(self, mwfn_result: MultiwfnResult) -> None:
    """Cache a :class:`MultiwfnResult` in memory.

    If :attr:`json_path` is not ``None``, the result is also
    written to the JSON file on disk.

    Parameters
    ----------
    mwfn_result
        A fully parsed ``MultiwfnResult`` whose ``.result`` list
        is non-empty.

    """
    if not mwfn_result.result:
        return

    entry: dict[str, Any] = {
        "parsed": mwfn_result.to_dict(),
        "timestamp": datetime.now().isoformat(),
    }
    self._data.setdefault("analyses", {})[mwfn_result.analysis.name] = (
        entry
    )
    self._save()

store_from_stdout(analysis, stdout)

Parse stdout, persist, and return the :class:MultiwfnResult.

Convenience method combining :meth:MultiwfnResult.parse and :meth:store in a single call.

Returns None if no parser is available or parsing yields no results.

Source code in src/pymultiwfn/analysis/result.py
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
def store_from_stdout(
    self,
    analysis: Menu,
    stdout: str,
) -> MultiwfnResult | None:
    """Parse *stdout*, persist, and return the :class:`MultiwfnResult`.

    Convenience method combining :meth:`MultiwfnResult.parse` and
    :meth:`store` in a single call.

    Returns ``None`` if no parser is available or parsing yields no
    results.
    """
    result = MultiwfnResult(analysis=analysis)
    result.parse(stdout)
    if not result.result:
        return None
    self.store(result)
    return result

Spectrum dataclass

Bases: ParsedMultiwfnResult

Result for spectrum analysis.

Source code in src/pymultiwfn/analysis/result.py
185
186
187
188
189
190
191
192
193
@dataclass
class Spectrum(ParsedMultiwfnResult):
    """Result for spectrum analysis."""

    frequencies: list[float] | None = None
    intensities: list[float] | None = None
    wavelengths: list[float] | None = None
    atom_indices: list[int] | None = None
    chemical_shifts: list[float] | None = None

SurfaceAnalysis dataclass

Bases: ParsedMultiwfnResult

Result for surface analysis.

Source code in src/pymultiwfn/analysis/result.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
@dataclass
class SurfaceAnalysis(ParsedMultiwfnResult):
    """Result for surface analysis."""

    area: float | None = None
    volume: float | None = None
    V_S_plus: float | None = None
    V_S_minus: float | None = None
    sigma2_total: float | None = None
    nu: float | None = None
    pi: float | None = None
    V_S_max: float | None = None
    V_S_min: float | None = None
    balance: float | None = None

SurfaceExtremum dataclass

Bases: ParsedMultiwfnResult

Result for surface extrema.

Source code in src/pymultiwfn/analysis/result.py
238
239
240
241
242
243
244
245
246
247
@dataclass
class SurfaceExtremum(ParsedMultiwfnResult):
    """Result for surface extrema."""

    type: Literal["min", "max"]
    index: int
    value: float
    x: float
    y: float
    z: float

Transition dataclass

Bases: ParsedMultiwfnResult

Result for transition analysis.

Source code in src/pymultiwfn/analysis/result.py
196
197
198
199
200
201
202
203
204
@dataclass
class Transition(ParsedMultiwfnResult):
    """Result for transition analysis."""

    state: int
    energy_eV: float  # noqa: N815
    wavelength_nm: float
    osc_strength: float
    rot_strength: float | None = None

Valence dataclass

Bases: ParsedMultiwfnResult

Result for valence analysis.

Source code in src/pymultiwfn/analysis/result.py
117
118
119
120
121
122
123
@dataclass
class Valence(ParsedMultiwfnResult):
    """Result for valence analysis."""

    atom_id: int
    type: Literal["total_valence", "free_valence"]
    valence: float

WeakInteraction dataclass

Bases: ParsedMultiwfnResult

Result for weak interaction analysis.

Source code in src/pymultiwfn/analysis/result.py
364
365
366
367
368
369
370
371
@dataclass
class WeakInteraction(ParsedMultiwfnResult):
    """Result for weak interaction analysis."""

    delta_g_inter: float = 0.0
    delta_g_intra: float = 0.0
    isosurface_integral: float | None = None
    cube_names: list[str] | None = None