Skip to content

Commit 54d769a

Browse files
committed
Py 14
1 parent 0659440 commit 54d769a

6 files changed

Lines changed: 91 additions & 75 deletions

File tree

arc/level/cbs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import ast
4242
import math
4343
import re
44-
from typing import Callable, Dict, Mapping
44+
from collections.abc import Callable, Mapping
4545

4646
import numpy as np
4747

@@ -221,7 +221,7 @@ def martin_3pt(energies: Mapping[int, float]) -> float:
221221

222222
# String → callable registry advertised to user input. New built-in formulas are
223223
# added by inserting an entry here (and a corresponding test).
224-
BUILTIN_FORMULAS: Dict[str, Callable[..., float]] = {
224+
BUILTIN_FORMULAS: dict[str, Callable[..., float]] = {
225225
"helgaker_corr_2pt": helgaker_corr_2pt,
226226
"helgaker_hf_2pt": helgaker_hf_2pt,
227227
"martin_3pt": martin_3pt,

arc/level/presets.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
import copy
2424
import os
25-
from typing import Any, Dict, List, Mapping, Optional
25+
from collections.abc import Mapping
26+
from typing import Any
2627

2728
import yaml
2829

@@ -33,7 +34,7 @@
3334
_PRESETS_PATH = os.path.join(_HERE, "presets.yml")
3435

3536

36-
def _load_presets(path: str) -> Dict[str, Dict[str, Any]]:
37+
def _load_presets(path: str) -> dict[str, dict[str, Any]]:
3738
"""Load ``presets.yml`` once; return the parsed mapping."""
3839
with open(path, "r") as fh:
3940
data = yaml.safe_load(fh) or {}
@@ -43,15 +44,15 @@ def _load_presets(path: str) -> Dict[str, Dict[str, Any]]:
4344

4445

4546
# Module-level cache. Loaded once at import time; a single source of truth.
46-
PRESETS: Dict[str, Dict[str, Any]] = _load_presets(_PRESETS_PATH)
47-
REGISTERED_PRESET_NAMES: List[str] = sorted(PRESETS.keys())
47+
PRESETS: dict[str, dict[str, Any]] = _load_presets(_PRESETS_PATH)
48+
REGISTERED_PRESET_NAMES: list[str] = sorted(PRESETS.keys())
4849

4950

5051
# Fields that may appear on a preset term by its ``type`` discriminator.
5152
# Used to reject typos in preset overrides (e.g. ``delta_T.hihg``). The key
5253
# ``"base"`` is not a term type — it's the protocol's base level dict, for
5354
# which we accept any Level-level keyword plus ``label``.
54-
_ALLOWED_OVERRIDE_FIELDS_BY_TYPE: Dict[str, set] = {
55+
_ALLOWED_OVERRIDE_FIELDS_BY_TYPE: dict[str, set] = {
5556
"single_point": {"label", "type", "level"},
5657
"delta": {"label", "type", "high", "low"},
5758
"cbs_extrapolation": {"label", "type", "formula", "components", "levels"},
@@ -69,7 +70,7 @@ def _load_presets(path: str) -> Dict[str, Dict[str, Any]]:
6970
}
7071

7172

72-
def _deep_merge_level_dict(target: Dict[str, Any], patch: Dict[str, Any]) -> None:
73+
def _deep_merge_level_dict(target: dict[str, Any], patch: dict[str, Any]) -> None:
7374
"""Shallow-merge ``patch`` into ``target`` with one level of nesting for
7475
``high``/``low``/``level`` — replacing fields of the inner dict rather than
7576
the whole dict. Mutates ``target`` in place.
@@ -94,8 +95,8 @@ def _deep_merge_level_dict(target: Dict[str, Any], patch: Dict[str, Any]) -> Non
9495
target[key] = new_val
9596

9697

97-
def _validate_override_fields(term_or_base: Dict[str, Any],
98-
patch: Dict[str, Any],
98+
def _validate_override_fields(term_or_base: dict[str, Any],
99+
patch: dict[str, Any],
99100
target_name: str) -> None:
100101
"""Reject typos in override patch keys.
101102
@@ -123,9 +124,9 @@ def _validate_override_fields(term_or_base: Dict[str, Any],
123124

124125

125126
def _apply_overrides(
126-
recipe: Dict[str, Any],
127+
recipe: dict[str, Any],
127128
overrides: Mapping[str, Any],
128-
) -> Dict[str, Any]:
129+
) -> dict[str, Any]:
129130
"""Merge per-term ``overrides`` into a recipe and return the result.
130131
131132
``overrides`` is a mapping ``{term_label: {field_name: new_value}}``. The
@@ -173,8 +174,8 @@ def _apply_overrides(
173174

174175
def expand_preset(
175176
name: str,
176-
overrides: Optional[Mapping[str, Any]] = None,
177-
) -> Dict[str, Any]:
177+
overrides: Mapping[str, Any] | None = None,
178+
) -> dict[str, Any]:
178179
"""Resolve a preset name (with optional overrides) to an independent recipe dict.
179180
180181
Parameters

arc/level/protocol.py

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141

4242
import copy
4343
from abc import ABC, abstractmethod
44-
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
44+
from collections.abc import Iterable
45+
from typing import Any
4546

4647
from arc.exceptions import InputError
4748
from arc.level.cbs import (
@@ -74,11 +75,11 @@ class Term(ABC):
7475
label: str
7576

7677
@abstractmethod
77-
def required_levels(self) -> List[Tuple[str, Level]]:
78+
def required_levels(self) -> list[tuple[str, Level]]:
7879
"""Return ``[(sub_label, Level), ...]`` pairs for every SP this term needs."""
7980

8081
@abstractmethod
81-
def evaluate(self, energies: Dict[str, float]) -> float:
82+
def evaluate(self, energies: dict[str, float]) -> float:
8283
"""Combine sub-job energies into this term's contribution.
8384
8485
The keys of ``energies`` are the ``sub_label`` strings yielded by
@@ -87,11 +88,11 @@ def evaluate(self, energies: Dict[str, float]) -> float:
8788
"""
8889

8990
@abstractmethod
90-
def as_dict(self) -> Dict[str, Any]:
91+
def as_dict(self) -> dict[str, Any]:
9192
"""Serialise to a JSON/YAML-friendly dict including a discriminator ``type``."""
9293

9394
@classmethod
94-
def from_dict(cls, data: Dict[str, Any]) -> "Term":
95+
def from_dict(cls, data: dict[str, Any]) -> "Term":
9596
"""Reconstruct a ``Term`` subclass from its serialised dict.
9697
9798
Dispatches on the ``type`` discriminator written by :meth:`as_dict`.
@@ -114,7 +115,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "Term":
114115
)
115116

116117

117-
def _coerce_level(value: Union[str, Dict[str, Any], Level]) -> Level:
118+
def _coerce_level(value: str | dict[str, Any] | Level) -> Level:
118119
"""Accept either a string, dict, or Level; return a Level instance."""
119120
if isinstance(value, Level):
120121
return value
@@ -128,27 +129,27 @@ def _coerce_level(value: Union[str, Dict[str, Any], Level]) -> Level:
128129
class SinglePointTerm(Term):
129130
"""One absolute single-point energy at one level of theory."""
130131

131-
def __init__(self, label: str, level: Union[str, Dict[str, Any], Level]):
132+
def __init__(self, label: str, level: str | dict[str, Any] | Level):
132133
if not label:
133134
raise InputError("SinglePointTerm requires a non-empty label.")
134135
self.label = label
135136
self.level = _coerce_level(level)
136137

137-
def required_levels(self) -> List[Tuple[str, Level]]:
138+
def required_levels(self) -> list[tuple[str, Level]]:
138139
return [(self.label, self.level)]
139140

140-
def evaluate(self, energies: Dict[str, float]) -> float:
141+
def evaluate(self, energies: dict[str, float]) -> float:
141142
return energies[self.label]
142143

143-
def as_dict(self) -> Dict[str, Any]:
144+
def as_dict(self) -> dict[str, Any]:
144145
return {
145146
"type": "single_point",
146147
"label": self.label,
147148
"level": self.level.as_dict(),
148149
}
149150

150151
@classmethod
151-
def _from_dict(cls, data: Dict[str, Any]) -> "SinglePointTerm":
152+
def _from_dict(cls, data: dict[str, Any]) -> "SinglePointTerm":
152153
return cls(label=data["label"], level=data["level"])
153154

154155

@@ -162,8 +163,8 @@ class DeltaTerm(Term):
162163
def __init__(
163164
self,
164165
label: str,
165-
high: Optional[Union[str, Dict[str, Any], Level]],
166-
low: Optional[Union[str, Dict[str, Any], Level]],
166+
high: str | dict[str, Any] | Level | None,
167+
low: str | dict[str, Any] | Level | None,
167168
):
168169
if not label:
169170
raise InputError("DeltaTerm requires a non-empty label.")
@@ -179,13 +180,13 @@ def __init__(
179180
def _sub(self, suffix: str) -> str:
180181
return f"{self.label}__{suffix}"
181182

182-
def required_levels(self) -> List[Tuple[str, Level]]:
183+
def required_levels(self) -> list[tuple[str, Level]]:
183184
return [(self._sub("high"), self.high), (self._sub("low"), self.low)]
184185

185-
def evaluate(self, energies: Dict[str, float]) -> float:
186+
def evaluate(self, energies: dict[str, float]) -> float:
186187
return energies[self._sub("high")] - energies[self._sub("low")]
187188

188-
def as_dict(self) -> Dict[str, Any]:
189+
def as_dict(self) -> dict[str, Any]:
189190
return {
190191
"type": "delta",
191192
"label": self.label,
@@ -194,7 +195,7 @@ def as_dict(self) -> Dict[str, Any]:
194195
}
195196

196197
@classmethod
197-
def _from_dict(cls, data: Dict[str, Any]) -> "DeltaTerm":
198+
def _from_dict(cls, data: dict[str, Any]) -> "DeltaTerm":
198199
return cls(label=data["label"], high=data["high"], low=data["low"])
199200

200201

@@ -241,7 +242,7 @@ def __init__(
241242
self,
242243
label: str,
243244
formula: str,
244-
levels: List[Union[str, Dict[str, Any], Level]],
245+
levels: list[str | dict[str, Any] | Level],
245246
components: str = "total",
246247
):
247248
if not label:
@@ -282,7 +283,7 @@ def __init__(
282283
# construction time catches "martin_3pt with 2 levels" before a sub-job
283284
# ever runs. When new built-ins are added, update this table alongside
284285
# the entry in arc.level.cbs.BUILTIN_FORMULAS.
285-
_BUILTIN_FORMULA_ARITY: Dict[str, int] = {
286+
_BUILTIN_FORMULA_ARITY: dict[str, int] = {
286287
"helgaker_corr_2pt": 2,
287288
"helgaker_hf_2pt": 2,
288289
"martin_3pt": 3,
@@ -336,14 +337,14 @@ def _user_fn(energies):
336337
def _sub(self, cardinal: int) -> str:
337338
return f"{self.label}__card_{cardinal}"
338339

339-
def required_levels(self) -> List[Tuple[str, Level]]:
340+
def required_levels(self) -> list[tuple[str, Level]]:
340341
return [(self._sub(c), lvl) for c, lvl in zip(self._cardinals, self.levels)]
341342

342-
def evaluate(self, energies: Dict[str, float]) -> float:
343+
def evaluate(self, energies: dict[str, float]) -> float:
343344
cardinal_to_energy = {c: energies[self._sub(c)] for c in self._cardinals}
344345
return self._formula_callable(cardinal_to_energy)
345346

346-
def as_dict(self) -> Dict[str, Any]:
347+
def as_dict(self) -> dict[str, Any]:
347348
return {
348349
"type": "cbs_extrapolation",
349350
"label": self.label,
@@ -353,7 +354,7 @@ def as_dict(self) -> Dict[str, Any]:
353354
}
354355

355356
@classmethod
356-
def _from_dict(cls, data: Dict[str, Any]) -> "CBSExtrapolationTerm":
357+
def _from_dict(cls, data: dict[str, Any]) -> "CBSExtrapolationTerm":
357358
return cls(
358359
label=data["label"],
359360
formula=data["formula"],
@@ -388,9 +389,9 @@ class CompositeProtocol:
388389
def __init__(
389390
self,
390391
base: SinglePointTerm,
391-
corrections: Optional[List[Term]] = None,
392-
preset_name: Optional[str] = None,
393-
reference: Optional[str] = None,
392+
corrections: list[Term] | None = None,
393+
preset_name: str | None = None,
394+
reference: str | None = None,
394395
):
395396
if not isinstance(base, SinglePointTerm):
396397
raise InputError(
@@ -409,7 +410,7 @@ def __init__(
409410
# A collision (e.g. SinglePointTerm(label='delta_T__high') plus a
410411
# DeltaTerm(label='delta_T', ...) whose 'high' sub-leg also ends up as
411412
# 'delta_T__high') would overwrite state silently. Reject at construction.
412-
sub_labels: List[str] = []
413+
sub_labels: list[str] = []
413414
for term in [base, *corrections]:
414415
for sub_label, _level in term.required_levels():
415416
sub_labels.append(sub_label)
@@ -427,22 +428,22 @@ def __init__(
427428
self.reference = reference
428429

429430
@property
430-
def terms(self) -> List[Term]:
431+
def terms(self) -> list[Term]:
431432
"""Convenience: ``[base, *corrections]`` in protocol order."""
432433
return [self.base, *self.corrections]
433434

434-
def evaluate(self, energies: Dict[str, float]) -> float:
435+
def evaluate(self, energies: dict[str, float]) -> float:
435436
"""Combine all sub-job energies into the protocol's electronic energy."""
436437
return sum(term.evaluate(energies) for term in self.terms)
437438

438-
def iter_required_jobs(self) -> Iterable[Tuple[str, str, Level]]:
439+
def iter_required_jobs(self) -> Iterable[tuple[str, str, Level]]:
439440
"""Yield ``(term_label, sub_label, Level)`` triples for every required SP."""
440441
for term in self.terms:
441442
for sub_label, level in term.required_levels():
442443
yield (term.label, sub_label, level)
443444

444-
def as_dict(self) -> Dict[str, Any]:
445-
out: Dict[str, Any] = {
445+
def as_dict(self) -> dict[str, Any]:
446+
out: dict[str, Any] = {
446447
"base": self.base.as_dict(),
447448
"corrections": [t.as_dict() for t in self.corrections],
448449
}
@@ -453,7 +454,7 @@ def as_dict(self) -> Dict[str, Any]:
453454
return out
454455

455456
@classmethod
456-
def from_dict(cls, data: Dict[str, Any]) -> "CompositeProtocol":
457+
def from_dict(cls, data: dict[str, Any]) -> "CompositeProtocol":
457458
"""Inverse of :meth:`as_dict`. Each entry must already include its discriminator."""
458459
if not isinstance(data, dict) or "base" not in data:
459460
raise InputError(
@@ -479,7 +480,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "CompositeProtocol":
479480
)
480481

481482
@classmethod
482-
def from_user_input(cls, raw: Union[str, Dict[str, Any]]) -> "CompositeProtocol":
483+
def from_user_input(cls, raw: str | dict[str, Any]) -> "CompositeProtocol":
483484
"""Accept the YAML-shaped user input and produce a validated protocol.
484485
485486
Three forms are accepted:
@@ -498,7 +499,7 @@ def from_user_input(cls, raw: Union[str, Dict[str, Any]]) -> "CompositeProtocol"
498499
arc.exceptions.InputError
499500
On any malformed input.
500501
"""
501-
preset_name: Optional[str] = None
502+
preset_name: str | None = None
502503
if isinstance(raw, str):
503504
preset_name = raw
504505
raw = expand_preset(raw)
@@ -549,7 +550,7 @@ def from_user_input(cls, raw: Union[str, Dict[str, Any]]) -> "CompositeProtocol"
549550
f"SinglePointTerm dict; got {type(base_raw).__name__}."
550551
)
551552

552-
corrections: List[Term] = []
553+
corrections: list[Term] = []
553554
for entry in raw.get("corrections", []):
554555
if not isinstance(entry, dict):
555556
raise InputError(

0 commit comments

Comments
 (0)