Skip to content

add argument for TBSystem to change eig_solver#325

Open
Lonya0 wants to merge 5 commits intodeepmodeling:mainfrom
Lonya0:main
Open

add argument for TBSystem to change eig_solver#325
Lonya0 wants to merge 5 commits intodeepmodeling:mainfrom
Lonya0:main

Conversation

@Lonya0
Copy link
Copy Markdown
Collaborator

@Lonya0 Lonya0 commented Apr 8, 2026

#324
and change override_overlap argument passing

Summary by CodeRabbit

  • Improvements
    • Added flexible control of eigenvalue computations: you can now pass solver choice and k-point mesh options through higher-level calls to control band and electronic-property calculations.
    • Band computations and Fermi-level estimation now accept and forward these solver/k-point options for consistent results.
    • Introduced explicit overlap-override handling (including an option to disable it) for more predictable postprocessing behavior.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

📝 Walkthrough

Walkthrough

Adds flexible keyword-argument propagation for eigenvalue/overlap configuration across postprocessing: ElecStruCal accepts **kwargs and normalizes override_overlap; the unified calculator interface and adapters accept **kwargs; accessors and system components forward nk/eig_solver via kwargs to eigenvalue computation.

Changes

Cohort / File(s) Summary
Overlap Configuration Enhancement
dptb/postprocess/elec_struc_cal.py
ElecStruCal.__init__ now accepts **kwargs and stores a string override_overlap when provided; get_data/get_eigs widen override_overlap to Union[str,bool,None] and normalize values (False→None, truthy→string, else fallback to instance value) before loading/initializing eigenvectors.
Eigenvalue Solver Interface Extension
dptb/postprocess/unified/calculator.py
Abstract HamiltonianCalculator.get_eigenvalues and DeePTBAdapter.get_eigenvalues now accept **kwargs; DeePTBAdapter derives nk and solver from kwargs (e.g., kwargs["nk"], kwargs["eig_solver"]/kwargs["solver"]) when explicit args are missing, then calls the eigensolver with resolved params.
Parameter Forwarding Through Accessors & System
dptb/postprocess/unified/properties/band.py, dptb/postprocess/unified/system.py
BandAccessor.compute and TBSystem methods (get_efermi, estimate_efermi_e, get_bands) accept/forward **kwargs and pass nk and eig_solver (from kwargs) into get_eigenvalues/_bands.compute to control eigenvalue computation.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant BandAccessor
  participant TBSystem
  participant Calculator
  participant EigSolver

  Client->>BandAccessor: compute(**kwargs with nk, eig_solver)
  BandAccessor->>TBSystem: request bands / get_efermi(**kwargs)
  TBSystem->>Calculator: get_eigenvalues(atomic_data, **kwargs)
  Calculator->>EigSolver: eigv_solver(data, nk=resolved_nk, eig_solver=resolved_solver)
  EigSolver-->>Calculator: eigenvalues
  Calculator-->>TBSystem: eigenvalues, metadata
  TBSystem-->>BandAccessor: bands / efermi
  BandAccessor-->>Client: bands (computed)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the pull request: enabling TBSystem to accept an argument for changing the eigenvalue solver (eig_solver).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
dptb/postprocess/elec_struc_cal.py (1)

107-123: ⚠️ Potential issue | 🔴 Critical

Preserve False as a real opt-out for overlap overrides.

Line 154 converts override_overlap=False into None, and Lines 107-123 then fall back to self.override_overlap again. On non-overlap models that also leaves self.eigv configured with overlap fields after the first overridden call, so a later opt-out can still use the default overlap file or fail because no overlap data was loaded. Please normalize once per call (None => instance default, False => disable, str => explicit path) and derive both the self.eigv configuration and load_data_for_model(..., override_overlap=...) from that normalized value.

Also applies to: 154-155

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dptb/postprocess/elec_struc_cal.py` around lines 107 - 123, Normalize the
incoming override_overlap once at the start of the call: treat None as "use
instance default" by setting normalized = self.override_overlap if
override_overlap is None else override_overlap; preserve False as a real opt-out
and keep strings/paths as explicit values. Use this normalized value to decide
whether to instantiate self.eigv (i.e., only create Eigenvalues when normalized
is not False and indicates overlap) and pass the same normalized value into
load_data_for_model(..., override_overlap=normalized) so you never convert False
to None or re-fallback to self.override_overlap later; update uses of
override_overlap/self.override_overlap and the Eigenvalues construction to
reference this one normalized variable.
dptb/postprocess/unified/system.py (2)

299-312: ⚠️ Potential issue | 🟠 Major

reuse=True makes the new band options inert after the first computation.

Once self.has_bands is set, a later get_bands(..., eig_solver="numpy") or different nk still returns the cached accessor from the earlier run because the reuse check ignores the new compute kwargs. The new API should either invalidate the cache when these options change or include them in the cache key.

🗂️ Track the band-compute options in the reuse check
-        if self.has_bands and reuse:
+        requested_band_kwargs = {
+            "nk": kwargs.get("nk", None),
+            "eig_solver": kwargs.get("eig_solver", None),
+        }
+        if self.has_bands and reuse and getattr(self, "_last_band_kwargs", None) == requested_band_kwargs:
             return self._bands
         else:
             assert kpath_config is not None, "kpath_config must be provided if bands not calculated."
             self._bands = BandAccessor(self)
             self._bands.set_kpath(**kpath_config)
-            self._bands.compute(nk=kwargs.get("nk", None),
-                                eig_solver=kwargs.get("eig_solver", None))
+            self._bands.compute(**requested_band_kwargs)
+            self._last_band_kwargs = requested_band_kwargs
             self.has_bands = True
             return self._bands
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dptb/postprocess/unified/system.py` around lines 299 - 312, get_bands
currently returns a cached BandAccessor (self._bands) whenever self.has_bands is
true regardless of new compute options (nk, eig_solver), causing new options to
be ignored; modify get_bands to detect when provided compute kwargs differ from
the options used to produce the cached accessor and either invalidate/recreate
the cache or re-run compute on the cached BandAccessor. Concretely: record the
last compute options on the BandAccessor (e.g.,
BandAccessor.last_compute_options) when compute(...) is called, then in
get_bands compare kwargs (nk, eig_solver and any other relevant keys) against
that record and if they differ and reuse is True either call
self._bands.compute(...) with the new options or set reuse=False/replace
self._bands so the new options take effect; ensure references to get_bands,
self._bands, BandAccessor.compute, and self.has_bands are updated accordingly.

243-251: ⚠️ Potential issue | 🔴 Critical

Don't pass calculator-only kwargs into estimate_efermi_e().

After the get_eigenvalues call, kwargs still contains nk/eig_solver, and Line 251 forwards them into estimate_efermi_e(), which accepts neither name. TBSystem.get_efermi(..., eig_solver="numpy") will raise TypeError instead of using the new feature.

🧩 Split solver kwargs from Fermi-level kwargs
-        data, eigs = self.calculator.get_eigenvalues(data,
-                                                     nk=kwargs.get("nk", None),
-                                                     eig_solver=kwargs.get("eig_solver", None))
+        solver_kwargs = {}
+        fermi_kwargs = dict(kwargs)
+        for key in ("nk", "eig_solver"):
+            value = fermi_kwargs.pop(key, None)
+            if value is not None:
+                solver_kwargs[key] = value
+
+        data, eigs = self.calculator.get_eigenvalues(data, **solver_kwargs)
 
         calculated_efermi = self.estimate_efermi_e(
                         eigenvalues=eigs.detach().numpy(),
                         temperature = temperature,
                         smearing_method=smearing_method,
-                        q_tol  = q_tol, **kwargs)
+                        q_tol  = q_tol, **fermi_kwargs)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dptb/postprocess/unified/system.py` around lines 243 - 251, The call to
estimate_efermi_e is accidentally receiving calculator-only kwargs (e.g., "nk"
and "eig_solver") that were meant for get_eigenvalues; update the code around
the get_eigenvalues / estimate_efermi_e sequence to separate solver kwargs from
Fermi-level kwargs — e.g., extract or copy kwargs, pop or exclude "nk" and
"eig_solver" before calling estimate_efermi_e (or build an explicit fermi_kwargs
dict with only the allowed keys), leaving get_eigenvalues(...) using
kwargs.get("nk"/"eig_solver") and calling
estimate_efermi_e(eigenvalues=eigs.detach().numpy(), temperature=temperature,
smearing_method=smearing_method, q_tol=q_tol, **fermi_kwargs) so no
calculator-only names are forwarded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dptb/postprocess/unified/properties/band.py`:
- Around line 235-237: The call to self._system.calculator.get_eigenvalues
currently always forwards nk and eig_solver with None which breaks compatibility
with calculators implementing the old get_eigenvalues(data) signature; instead,
build a small kwargs dict (e.g., eig_kwargs) conditionally: add 'nk' only if
'nk' in kwargs and add 'eig_solver' only if 'eig_solver' in kwargs, then call
self._system.calculator.get_eigenvalues(data, **eig_kwargs). This preserves
compatibility with TBSystem calculators while still forwarding explicitly
provided arguments.

---

Outside diff comments:
In `@dptb/postprocess/elec_struc_cal.py`:
- Around line 107-123: Normalize the incoming override_overlap once at the start
of the call: treat None as "use instance default" by setting normalized =
self.override_overlap if override_overlap is None else override_overlap;
preserve False as a real opt-out and keep strings/paths as explicit values. Use
this normalized value to decide whether to instantiate self.eigv (i.e., only
create Eigenvalues when normalized is not False and indicates overlap) and pass
the same normalized value into load_data_for_model(...,
override_overlap=normalized) so you never convert False to None or re-fallback
to self.override_overlap later; update uses of
override_overlap/self.override_overlap and the Eigenvalues construction to
reference this one normalized variable.

In `@dptb/postprocess/unified/system.py`:
- Around line 299-312: get_bands currently returns a cached BandAccessor
(self._bands) whenever self.has_bands is true regardless of new compute options
(nk, eig_solver), causing new options to be ignored; modify get_bands to detect
when provided compute kwargs differ from the options used to produce the cached
accessor and either invalidate/recreate the cache or re-run compute on the
cached BandAccessor. Concretely: record the last compute options on the
BandAccessor (e.g., BandAccessor.last_compute_options) when compute(...) is
called, then in get_bands compare kwargs (nk, eig_solver and any other relevant
keys) against that record and if they differ and reuse is True either call
self._bands.compute(...) with the new options or set reuse=False/replace
self._bands so the new options take effect; ensure references to get_bands,
self._bands, BandAccessor.compute, and self.has_bands are updated accordingly.
- Around line 243-251: The call to estimate_efermi_e is accidentally receiving
calculator-only kwargs (e.g., "nk" and "eig_solver") that were meant for
get_eigenvalues; update the code around the get_eigenvalues / estimate_efermi_e
sequence to separate solver kwargs from Fermi-level kwargs — e.g., extract or
copy kwargs, pop or exclude "nk" and "eig_solver" before calling
estimate_efermi_e (or build an explicit fermi_kwargs dict with only the allowed
keys), leaving get_eigenvalues(...) using kwargs.get("nk"/"eig_solver") and
calling estimate_efermi_e(eigenvalues=eigs.detach().numpy(),
temperature=temperature, smearing_method=smearing_method, q_tol=q_tol,
**fermi_kwargs) so no calculator-only names are forwarded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a5b75510-6833-4147-bef2-9a7abaed6897

📥 Commits

Reviewing files that changed from the base of the PR and between f00da65 and 4500b5e.

📒 Files selected for processing (4)
  • dptb/postprocess/elec_struc_cal.py
  • dptb/postprocess/unified/calculator.py
  • dptb/postprocess/unified/properties/band.py
  • dptb/postprocess/unified/system.py

Comment on lines +235 to +237
data, eigs = self._system.calculator.get_eigenvalues(data,
nk=kwargs.get("nk", None),
eig_solver=kwargs.get("eig_solver", None))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Only forward solver kwargs when they were explicitly requested.

TBSystem still allows duck-typed calculators, so always calling get_eigenvalues(..., nk=None, eig_solver=None) is a compatibility regression for calculators that still implement the old get_eigenvalues(data) signature. Build the kwargs dict conditionally before the call.

🔧 Safer forwarding
-        data, eigs = self._system.calculator.get_eigenvalues(data,
-                                                             nk=kwargs.get("nk", None),
-                                                             eig_solver=kwargs.get("eig_solver", None))
+        calc_kwargs = {}
+        if kwargs.get("nk") is not None:
+            calc_kwargs["nk"] = kwargs["nk"]
+        if kwargs.get("eig_solver") is not None:
+            calc_kwargs["eig_solver"] = kwargs["eig_solver"]
+
+        data, eigs = self._system.calculator.get_eigenvalues(data, **calc_kwargs)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dptb/postprocess/unified/properties/band.py` around lines 235 - 237, The call
to self._system.calculator.get_eigenvalues currently always forwards nk and
eig_solver with None which breaks compatibility with calculators implementing
the old get_eigenvalues(data) signature; instead, build a small kwargs dict
(e.g., eig_kwargs) conditionally: add 'nk' only if 'nk' in kwargs and add
'eig_solver' only if 'eig_solver' in kwargs, then call
self._system.calculator.get_eigenvalues(data, **eig_kwargs). This preserves
compatibility with TBSystem calculators while still forwarding explicitly
provided arguments.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
dptb/postprocess/unified/system.py (1)

300-303: Consider forwarding **kwargs directly in get_bands for extensibility.

get_bands now accepts **kwargs, but Line 312 forwards only nk/eig_solver. Passing through **kwargs keeps this method aligned with the unified flexible-kwargs direction and avoids future key plumbing edits.

Refactor suggestion
-            self._bands.compute(nk=kwargs.get("nk", None),
-                                eig_solver=kwargs.get("eig_solver", None))
+            self._bands.compute(**kwargs)

Also applies to: 312-313

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dptb/postprocess/unified/system.py` around lines 300 - 303, The get_bands
function currently accepts **kwargs but only forwards specific keys
(nk/eig_solver) when calling the downstream method; change the call inside
get_bands (the block that currently passes nk and eig_solver) to forward
**kwargs as well (or instead) so all additional parameters are propagated;
update the invocation in get_bands to include **kwargs when calling the
underlying routine (referencing get_bands) to preserve extensibility and avoid
future plumbing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dptb/postprocess/unified/system.py`:
- Around line 243-245: The call to self.calculator.get_eigenvalues currently
injects nk and eig_solver unconditionally (using kwargs.get("nk", None) and
kwargs.get("eig_solver", None)), which can cause TypeError for calculator
implementations that don't accept those keywords; change the call to build a
filtered kwargs dict (e.g., include "nk" and "eig_solver" only if kwargs
contains non-None values) and pass that dict (or explicitly pass only the
provided parameters) to self.calculator.get_eigenvalues so you only supply
supported keywords.

---

Nitpick comments:
In `@dptb/postprocess/unified/system.py`:
- Around line 300-303: The get_bands function currently accepts **kwargs but
only forwards specific keys (nk/eig_solver) when calling the downstream method;
change the call inside get_bands (the block that currently passes nk and
eig_solver) to forward **kwargs as well (or instead) so all additional
parameters are propagated; update the invocation in get_bands to include
**kwargs when calling the underlying routine (referencing get_bands) to preserve
extensibility and avoid future plumbing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1982fbdd-2a60-49e0-a62a-672ae3d165a4

📥 Commits

Reviewing files that changed from the base of the PR and between 4500b5e and ccf43d3.

📒 Files selected for processing (1)
  • dptb/postprocess/unified/system.py

Comment on lines +243 to +245
data, eigs = self.calculator.get_eigenvalues(data,
nk=kwargs.get("nk", None),
eig_solver=kwargs.get("eig_solver", None))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid unconditional keyword injection into protocol calculators.

At Line 243, get_eigenvalues is called with nk= and eig_solver= unconditionally (even when both are None). Since Line 46 allows any calculator that merely has get_eigenvalues, this can raise TypeError for valid protocol implementations that don’t accept those keywords.

Proposed compatibility-safe patch
-        data, eigs = self.calculator.get_eigenvalues(data,
-                                                     nk=kwargs.get("nk", None),
-                                                     eig_solver=kwargs.get("eig_solver", None))
+        eigen_kwargs = {}
+        nk = kwargs.get("nk", None)
+        eig_solver = kwargs.get("eig_solver", kwargs.get("solver", None))
+        if nk is not None:
+            eigen_kwargs["nk"] = nk
+        if eig_solver is not None:
+            eigen_kwargs["eig_solver"] = eig_solver
+        data, eigs = self.calculator.get_eigenvalues(data, **eigen_kwargs)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data, eigs = self.calculator.get_eigenvalues(data,
nk=kwargs.get("nk", None),
eig_solver=kwargs.get("eig_solver", None))
eigen_kwargs = {}
nk = kwargs.get("nk", None)
eig_solver = kwargs.get("eig_solver", kwargs.get("solver", None))
if nk is not None:
eigen_kwargs["nk"] = nk
if eig_solver is not None:
eigen_kwargs["eig_solver"] = eig_solver
data, eigs = self.calculator.get_eigenvalues(data, **eigen_kwargs)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dptb/postprocess/unified/system.py` around lines 243 - 245, The call to
self.calculator.get_eigenvalues currently injects nk and eig_solver
unconditionally (using kwargs.get("nk", None) and kwargs.get("eig_solver",
None)), which can cause TypeError for calculator implementations that don't
accept those keywords; change the call to build a filtered kwargs dict (e.g.,
include "nk" and "eig_solver" only if kwargs contains non-None values) and pass
that dict (or explicitly pass only the provided parameters) to
self.calculator.get_eigenvalues so you only supply supported keywords.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant