Skip to content

Conversation

@kavanase
Copy link
Member

Closes #85

Will merge to main and release after the next doped release (hopefully pretty soon).

@coderabbitai
Copy link

coderabbitai bot commented Apr 26, 2025

Walkthrough

This set of changes introduces new documentation and workflow resources for handling polaronic distortions in materials using ShakeNBreak, including a new Jupyter tutorial and a helper script for identifying hole states in VASP calculations. The core codebase is updated to improve neighbor-finding and distortion application logic, particularly enhancing support for complex, low-symmetry structures by using PeriodicNeighbor objects and batch distance calculations. Progress bar feedback is added to the distortion generation process. The test suite is expanded with a case verifying correct fallback behavior when insufficient neighbors are found, directly addressing issues with complex interface structures.

Changes

File(s) Change Summary
docs/ShakeNBreak_Polaron_Workflow.ipynb, docs/hole-finder.py, docs/CuSbSe2_POSCAR Added new polaron-focused tutorial notebook, a helper script for hole state identification, and a POSCAR file for CuSbSe2 structure.
docs/ShakeNBreak_Example_Workflow.ipynb, docs/index.rst, docs/Tutorials.rst, README.md Updated documentation: added/linked new tutorials, expanded study references, and updated documentation links.
shakenbreak/distortions.py Refactored neighbor-finding logic to use PeriodicNeighbor, batch distance calculations, and improved fallback in dimer distortion.
shakenbreak/input.py Added a tqdm progress bar to the distortion application loop for user feedback; simplified dictionary construction style.
tests/test_distortions.py Added a test to verify correct fallback neighbor logic in dimer distortion for structures with fewer than two CrystalNN neighbors.
shakenbreak/analysis.py Simplified conditional logic in distortion name formatting function for clarity.
shakenbreak/cli.py Clarified parentheses in file filtering condition to ensure intended logic grouping.
shakenbreak/energy_lowering_distortions.py Simplified pseudopotential dictionary construction using dict.fromkeys.
tests/test_cli.py Disabled an assertion comparing INCAR objects; added CliRunner with mix_stderr=False to suppress tqdm output during tests.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Distortions (shakenbreak.input)
    participant tqdm
    participant distortions.py

    User->>Distortions: apply_distortions()
    Distortions->>tqdm: Wrap defects_dict.items() iteration
    loop For each defect
        Distortions->>distortions.py: apply_dimer_distortion()
        distortions.py->>distortions.py: CrystalNN finds neighbors
        alt <2 neighbors found
            distortions.py->>distortions.py: _get_nns_to_distort() fallback
            distortions.py->>distortions.py: Use PeriodicNeighbor for neighbors
        end
        distortions.py-->>Distortions: Return distorted structure
    end
    Distortions-->>User: Distorted structures with progress feedback
Loading

Assessment against linked issues

Objective Addressed Explanation
Support distortion generation for complex, low-symmetry structures (e.g., interfaces, P1 symmetry) using doped and shakenbreak (#85)
Resolve AttributeError: 'index' attribute not found on PeriodicSite in neighbor sorting for distortion application (#85)
Ensure fallback logic in distortion application works for cases with <2 CrystalNN neighbors (#85)
Provide guidance and resources for applying distortions to complex structures and identifying polaronic states (#85)

Poem

A rabbit with code in its paws,
Hopped through structures with barely a pause.
With neighbors now found, and progress bars sound,
Polarons and holes are no longer lost underground.
Docs and tests multiply, as new workflows arise—
ShakeNBreak leaps, with bright, clever eyes!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 3

🧹 Nitpick comments (13)
docs/hole-finder.py (4)

1-7: Missing docstring for the module.

While the imports are appropriate for the functionality, the module is missing a docstring that would explain its purpose and usage.

Add a module-level docstring that explains what this script does and how to use it:

+"""
+Helper script to identify hole states in VASP defect calculations.
+
+This script analyzes the vasprun.xml output from a VASP calculation to locate
+single hole states in the electronic structure, which is useful for polaron studies.
+"""
 from pymatgen.io.vasp.outputs import BSVasprun
 from pymatgen.electronic_structure.core import Spin
 import pandas as pd
 import warnings

 warnings.filterwarnings("ignore", message="No POTCAR file with matching TITEL fields")
🧰 Tools
🪛 Ruff (0.8.2)

1-1: Missing docstring in public module

(D100)


38-40: Use a more descriptive DataFrame variable name.

The generic variable name 'df' for DataFrames is discouraged as it lacks descriptive context.

Use a more descriptive variable name for the DataFrame:

-    df = pd.DataFrame(eigenvals,
+    eigenval_df = pd.DataFrame(eigenvals,
                      columns=["band", "k-point", "up-eigenval", "down-eigenval", "up-occupancy",
                               "down-occupancy"])
🧰 Tools
🪛 Ruff (0.8.2)

38-38: Avoid using the generic variable name df for DataFrames

(PD901)


54-64: Boolean comparisons can be simplified.

The comparisons with == True and == False are redundant and can be simplified. Additionally, the logic for handling the two possible hole spin channels could be more concise.

Simplify the boolean comparisons and refactor the conditional logic:

-    if hole_spin_up.empty == True and hole_spin_down.empty == False:
+    if hole_spin_up.empty and not hole_spin_down.empty:
         print("Hole is in the spin down channel:\n", hole_spin_down)
         print("Recommended min and max for EINT in PARCHG calculation:",
               round(hole_spin_down["down-eigenval"].min(), 2),
               round(hole_spin_down["down-eigenval"].max(), 2))
-    elif hole_spin_down.empty == True and hole_spin_up.empty == False:
+    elif hole_spin_down.empty and not hole_spin_up.empty:
         print("Hole is in the spin up channel:\n", hole_spin_up)
         print("Recommended min and max for EINT in PARCHG calculation:",
               round(hole_spin_up["down-eigenval"].min(), 2), round(hole_spin_up["down-eigenval"].max(), 2))
-    elif hole_spin_up.empty == True and hole_spin_down.empty == True:
+    elif hole_spin_up.empty and hole_spin_down.empty:
         print("No holes found!")
🧰 Tools
🪛 Ruff (0.8.2)

54-54: Avoid equality comparisons to True; use if hole_spin_up.empty: for truth checks

Replace with hole_spin_up.empty

(E712)


54-54: Avoid equality comparisons to False; use if not hole_spin_down.empty: for false checks

Replace with not hole_spin_down.empty

(E712)


59-59: Avoid equality comparisons to True; use if hole_spin_down.empty: for truth checks

Replace with hole_spin_down.empty

(E712)


59-59: Avoid equality comparisons to False; use if not hole_spin_up.empty: for false checks

Replace with not hole_spin_up.empty

(E712)


63-63: Avoid equality comparisons to True; use if hole_spin_up.empty: for truth checks

Replace with hole_spin_up.empty

(E712)


63-63: Avoid equality comparisons to True; use if hole_spin_down.empty: for truth checks

Replace with hole_spin_down.empty

(E712)


66-67: Main guard implementation is straightforward.

The main guard uses a default filename that users would need to modify. Consider adding argparse support for better command-line usage.

Add command-line argument parsing for better usability:

+import argparse

 if __name__ == "__main__":
-    hole_finder("vasprun.xml")  # insert your vasprun.xml file here
+    parser = argparse.ArgumentParser(description="Find holes in VASP electronic structure")
+    parser.add_argument("vasprun", nargs="?", default="vasprun.xml",
+                        help="Path to vasprun.xml file (default: vasprun.xml)")
+    parser.add_argument("--bands-below", type=int, default=3,
+                        help="Number of bands below VBM to analyze (default: 3)")
+    parser.add_argument("--bands-above", type=int, default=3,
+                        help="Number of bands above VBM to analyze (default: 3)")
+    args = parser.parse_args()
+    
+    hole_finder(args.vasprun, args.bands_below, args.bands_above)
docs/ShakeNBreak_Example_Workflow.ipynb (1)

37-42: Informative tip addition looks good
The new {tip} block effectively highlights the polaron workflow tutorial and broadens the use cases for ShakeNBreak.

Nitpick: to keep tutorial naming consistent across the docs, consider renaming the link text from “SnB Polarons Tutorial” to “ShakeNBreak Polaron Workflow Tutorial” (or similar).

README.md (1)

90-97: Update publication list with recent papers using ShakeNBreak

The list of studies using ShakeNBreak has been expanded with recent publications, which is valuable for showcasing the tool's impact and providing reference materials for users.

For consistency with the rest of the list, consider using asterisks instead of dashes for these new bullet points to follow the markdownlint style guideline (MD004):

-          - Y\. Liu **_Small hole polarons in yellow phase δ-CsPbI<sub>3</sub>_** [_arXiv_](https://doi.org/10.48550/arXiv.2501.16695) 2025
-          - S. R. Kavanagh et al. **_Intrinsic point defect tolerance in selenium for indoor and tandem photovoltaics_** [_Energy & Environmental Science_](https://doi.org/10.1039/D4EE04647A) 2025
-          - J. Huang et al. **_Manganese in β-Ga<sub>2</sub>O<sub>3</sub>: a deep acceptor with a large nonradiative electron capture cross-section_** [_Journal of Physics D: Applied Physics_](https://doi.org/10.1088/1361-6463/adca42) 2025
-          - R. Desai et al. **_Exploring the Defect Landscape and Dopability of Chalcogenide Perovskite BaZrS<sub>3</sub>_** [_Journal of Physical Chemistry C_](https://doi.org/10.1021/acs.jpcc.5c01597) 2025
-          - C. Kaewmeechai, J. Strand & A. Shluger **_Structure and Migration Mechanisms of Oxygen Interstitial Defects in β-Ga<sub>2</sub>O<sub>3</sub>_** [_Physica Status Solidi B_](https://onlinelibrary.wiley.com/doi/10.1002/pssb.202400652) 2025 <!-- though didn't cite properly SnB or doped code papers... -->
+          * Y\. Liu **_Small hole polarons in yellow phase δ-CsPbI<sub>3</sub>_** [_arXiv_](https://doi.org/10.48550/arXiv.2501.16695) 2025
+          * S. R. Kavanagh et al. **_Intrinsic point defect tolerance in selenium for indoor and tandem photovoltaics_** [_Energy & Environmental Science_](https://doi.org/10.1039/D4EE04647A) 2025
+          * J. Huang et al. **_Manganese in β-Ga<sub>2</sub>O<sub>3</sub>: a deep acceptor with a large nonradiative electron capture cross-section_** [_Journal of Physics D: Applied Physics_](https://doi.org/10.1088/1361-6463/adca42) 2025
+          * R. Desai et al. **_Exploring the Defect Landscape and Dopability of Chalcogenide Perovskite BaZrS<sub>3</sub>_** [_Journal of Physical Chemistry C_](https://doi.org/10.1021/acs.jpcc.5c01597) 2025
+          * C. Kaewmeechai, J. Strand & A. Shluger **_Structure and Migration Mechanisms of Oxygen Interstitial Defects in β-Ga<sub>2</sub>O<sub>3</sub>_** [_Physica Status Solidi B_](https://onlinelibrary.wiley.com/doi/10.1002/pssb.202400652) 2025 <!-- though didn't cite properly SnB or doped code papers... -->
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

90-90: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


91-91: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


92-92: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


93-93: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


94-94: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


95-95: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


96-96: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


97-97: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

tests/test_distortions.py (2)

89-89: Load new test data for interface structure

The test now loads a silicon vacancy defect in a HfSiO interface structure, which will be used to test the fallback behavior.

The line exceeds the recommended line length (118 > 107). Consider splitting it for better readability:

-        self.V_Si_HfSiO_interface_entry = loadfn(os.path.join(self.DATA_DIR, "v_Si_C1_O1.70_HfSiO_interface_+1.json"))
+        self.V_Si_HfSiO_interface_entry = loadfn(
+            os.path.join(self.DATA_DIR, "v_Si_C1_O1.70_HfSiO_interface_+1.json")
+        )
🧰 Tools
🪛 Ruff (0.8.2)

89-89: Line too long (118 > 107)

(E501)


424-442: New test for dimer distortion fallback behavior

This test addresses the bug reported in issue #85, verifying that the apply_dimer_distortion function correctly falls back to using _get_nns_to_distort when CrystalNN returns fewer than two neighbors. This is crucial for handling complex, low-symmetry structures where neighbor-finding can be challenging.

Consider addressing the code style issues reported by the linter:

-        self.assertEqual(output["undistorted_structure"], self.V_Si_HfSiO_interface_entry.defect_supercell)
-        self.assertEqual(output["num_distorted_neighbours"], 2)
-        self.assertTrue(output["distorted_atoms"] == [[82, 'Si'], [177, 'O']])
-        self.assertTrue(min_dist > 1.5)  # Si-O dimer bond length is larger than min_dist in orig structure
+        assert output["undistorted_structure"] == self.V_Si_HfSiO_interface_entry.defect_supercell
+        assert output["num_distorted_neighbours"] == 2
+        assert output["distorted_atoms"] == [[82, "Si"], [177, "O"]]
+        assert min_dist > 1.5  # Si-O dimer bond length is larger than min_dist in orig structure
🧰 Tools
🪛 Ruff (0.8.2)

436-436: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


437-437: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


438-438: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


438-438: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


438-438: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


442-442: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)

docs/ShakeNBreak_Polaron_Workflow.ipynb (5)

65-70: Consider adding file path validation for better error handling.

The code loads the structure from a file without checking if it exists. For a more robust tutorial, consider adding a file existence check and appropriate error handling to guide users if the file is missing.

-bulk_supercell = Structure.from_file("CuSbSe2_POSCAR")  # load our bulk supercell structure
+import os
+
+poscar_path = "CuSbSe2_POSCAR"
+if not os.path.exists(poscar_path):
+    raise FileNotFoundError(f"Structure file {poscar_path} not found. Please ensure it's in the same directory as this notebook.")
+    
+bulk_supercell = Structure.from_file(poscar_path)  # load our bulk supercell structure

257-259: Consider clarifying the PeriodicSite tolerance value choice.

The code sets a specific tolerance value for position matching without explaining why 1e-3 was chosen. Adding a brief comment would help users understand when they might need to adjust this value.

-PeriodicSite.position_atol = 1e-3  # update position tol to ensure site-matching works
+# Set position tolerance to 1e-3 Angstroms to ensure reliable site-matching 
+# between structures with slight position differences after symmetry analysis
+PeriodicSite.position_atol = 1e-3

325-336: Consider refactoring to avoid code duplication.

The two update blocks perform very similar operations. Consider using a function or loop to reduce duplication.

-trial_hole_polaron_supercells.update({f"Se_Cs_Cu2.41_{distortion_factor:+.1%}": distort_and_rattle(
-    structure=bulk_supercell,
-    site_index=Se_Cs_Cu2pt41_site_idx,
-    num_nearest_neighbours=4,  # let's distort the 4 nearest neighbours
-    distortion_factor=distortion_factor,
-)[\"distorted_structure\"] for distortion_factor in [-0.4, 0.4]})
-trial_hole_polaron_supercells.update({f"Se_Cs_Cu2.44_{distortion_factor:+.1%}": distort_and_rattle(
-    structure=bulk_supercell,
-    site_index=Se_Cs_Cu2pt44_site_idx,
-    num_nearest_neighbours=4,  # let's distort the 4 nearest neighbours
-    distortion_factor=distortion_factor,
-)[\"distorted_structure\"] for distortion_factor in [-0.4, 0.4]})
+# Create distortion for each Se site type
+se_sites = {
+    "Se_Cs_Cu2.41": Se_Cs_Cu2pt41_site_idx,
+    "Se_Cs_Cu2.44": Se_Cs_Cu2pt44_site_idx
+}
+
+for site_name, site_idx in se_sites.items():
+    trial_hole_polaron_supercells.update({
+        f"{site_name}_{distortion_factor:+.1%}": distort_and_rattle(
+            structure=bulk_supercell,
+            site_index=site_idx,
+            num_nearest_neighbours=4,  # let's distort the 4 nearest neighbours
+            distortion_factor=distortion_factor,
+        )["distorted_structure"] 
+        for distortion_factor in [-0.4, 0.4]
+    })

357-358: Add error handling for file writing operations.

Consider adding error handling to catch potential issues when writing files.

-for name, structure in trial_hole_polaron_supercells.items():
-    structure.to(fmt="POSCAR", filename=f"{name}_POSCAR")
+import os
+
+for name, structure in trial_hole_polaron_supercells.items():
+    filename = f"{name}_POSCAR"
+    try:
+        structure.to(fmt="POSCAR", filename=filename)
+        print(f"Successfully wrote {filename}")
+    except Exception as e:
+        print(f"Error writing {filename}: {e}")

372-373: Consider using Python for file listing instead of shell command.

For better cross-platform compatibility, consider using Python's os.listdir() or glob module instead of the shell command.

-!ls *_POSCAR  # check the POSCARs have been written
+import glob
+poscar_files = glob.glob("*_POSCAR")
+print("\n".join(sorted(poscar_files)))  # check the POSCARs have been written
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bed6e0f and bdeed05.

📒 Files selected for processing (10)
  • README.md (2 hunks)
  • docs/CuSbSe2_POSCAR (1 hunks)
  • docs/ShakeNBreak_Example_Workflow.ipynb (1 hunks)
  • docs/ShakeNBreak_Polaron_Workflow.ipynb (1 hunks)
  • docs/Tutorials.rst (1 hunks)
  • docs/hole-finder.py (1 hunks)
  • docs/index.rst (2 hunks)
  • shakenbreak/distortions.py (3 hunks)
  • shakenbreak/input.py (2 hunks)
  • tests/test_distortions.py (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/test_distortions.py (1)
shakenbreak/distortions.py (1)
  • apply_dimer_distortion (351-478)
🪛 Ruff (0.8.2)
docs/hole-finder.py

1-1: Missing docstring in public module

(D100)


9-9: Missing docstring in public function

(D103)


38-38: Avoid using the generic variable name df for DataFrames

(PD901)


47-47: Line too long (118 > 107)

(E501)


49-49: Line too long (108 > 107)

(E501)


54-54: Avoid equality comparisons to True; use if hole_spin_up.empty: for truth checks

Replace with hole_spin_up.empty

(E712)


54-54: Avoid equality comparisons to False; use if not hole_spin_down.empty: for false checks

Replace with not hole_spin_down.empty

(E712)


59-59: Avoid equality comparisons to True; use if hole_spin_down.empty: for truth checks

Replace with hole_spin_down.empty

(E712)


59-59: Avoid equality comparisons to False; use if not hole_spin_up.empty: for false checks

Replace with not hole_spin_up.empty

(E712)


63-63: Avoid equality comparisons to True; use if hole_spin_up.empty: for truth checks

Replace with hole_spin_up.empty

(E712)


63-63: Avoid equality comparisons to True; use if hole_spin_down.empty: for truth checks

Replace with hole_spin_down.empty

(E712)

tests/test_distortions.py

89-89: Line too long (118 > 107)

(E501)


436-436: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


437-437: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


438-438: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


438-438: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


438-438: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


442-442: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)

🪛 markdownlint-cli2 (0.17.2)
README.md

90-90: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


91-91: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


92-92: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


93-93: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


94-94: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


95-95: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


96-96: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


97-97: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

🪛 GitHub Actions: Linting
shakenbreak/input.py

[error] 2705-2705: Ruff E501: Line too long (119 > 107).

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: build (macos-14, 3.12)
  • GitHub Check: build (macos-14, 3.11)
  • GitHub Check: build (macos-14, 3.10)
  • GitHub Check: build (ubuntu-latest, 3.12)
  • GitHub Check: build (ubuntu-latest, 3.11)
  • GitHub Check: build (ubuntu-latest, 3.10)
🔇 Additional comments (18)
shakenbreak/input.py (2)

38-39: Appropriately added tqdm import for progress bar functionality.

The import of tqdm is added at the correct location with the other third-party imports, maintaining the organized import structure of the file.


2338-2342: Progress bar addition improves user experience.

Adding a progress bar using tqdm provides helpful feedback to users during potentially long-running distortion generation steps. The implementation includes percentage completion, elapsed time, and processing rate, which helps users monitor the execution progress.

docs/hole-finder.py (1)

51-52: Concise but effective filtering to identify hole states.

The approach to identify holes by filtering for specific occupancy patterns (1.0 in one spin channel, 0.0 in the other) is effective.

docs/CuSbSe2_POSCAR (1)

1-72: Well-structured POSCAR file with oxidation states.

The POSCAR file provides a complete and well-formatted crystal structure for CuSbSe2, including explicit oxidation states for Cu+, Sb3+, and Se2-. The structure appears to be a supercell with proper lattice vectors and atomic positions in direct coordinates. All necessary components are included for using this file in VASP calculations.

docs/Tutorials.rst (2)

9-10: Added link to new polaron tutorial.

Documentation has been appropriately updated to include a link to the new polaron tutorial alongside the existing python API tutorial. The links are properly formatted with full URLs.


17-17: Added polaron workflow to toctree.

The toctree directive has been updated to include the new ShakeNBreak_Polaron_Workflow notebook, ensuring it will be properly indexed and accessible in the documentation.

README.md (1)

61-61: URL improvement for Python API tutorial

The link to the Python API tutorial has been updated to point directly to the documentation site, which is more user-friendly.

docs/index.rst (2)

110-113: Added link to new polarons tutorial

The Python API documentation has been enhanced by adding a reference to the new polarons tutorial, which expands the documented capabilities of ShakeNBreak to include polaron workflows.


152-159: Consistent updates to publication list

The list of studies has been updated to mirror the changes in the README, maintaining documentation consistency across files.

tests/test_distortions.py (1)

1-3: Helpful module docstring addition

Adding a module-level docstring helps clarify test coverage and indicates that most distortion functions are already implicitly tested elsewhere.

shakenbreak/distortions.py (3)

13-13: Add import for PeriodicNeighbor class

Added the PeriodicNeighbor import which is needed for the improved neighbor object construction in the fallback mechanism.


168-177: Performance improvement in neighbor distance calculation

Improved the efficiency of neighbor distance calculations by using a single batch call to get_distances() instead of computing distances repeatedly inside a list comprehension.


415-425: Enhanced fallback logic for dimer distortion

Improved the robustness of the apply_dimer_distortion function when CrystalNN fails to find enough neighbors by creating proper PeriodicNeighbor objects with precomputed distances. This directly addresses the issue reported in #85, ensuring that complex structures with challenging neighbor-finding scenarios are handled correctly.

docs/ShakeNBreak_Polaron_Workflow.ipynb (5)

96-102: LGTM! Well-documented code example for atomic rattling.

The code demonstrates the rattling functionality well, with helpful comments about additional options.


134-138: LGTM! Clear demonstration of using SpacegroupAnalyzer.

The symmetry analysis is correctly implemented to identify inequivalent sites.


181-185: LGTM! Proper demonstration of the DefectsGenerator.

The implementation correctly shows how to use doped's DefectsGenerator without interstitials or supercell generation.


280-287: LGTM! Well-structured distortion implementation.

The code clearly demonstrates the use of distort_and_rattle for generating trial polaron structures, with appropriate distortion factors and nearest neighbor count.


436-452: Update kernel information to match Python version requirements.

The metadata indicates Python 2.7.6 (line 451) but the code appears to be using Python 3 features. Consider updating the notebook metadata to reflect the correct Python version requirements for clarity.

Please verify the minimum Python version required for this tutorial and ensure the notebook metadata reflects this correctly.

Comment on lines +9 to +15
def hole_finder(vasprun_defect, bands_below=3, bands_above=3):
warnings.warn("""Python is 0-indexed, VASP in 1-indexed. All k-points and
bands shown as an output from this script are 1-indexed, i.e. VASP-friendly.""")

warnings.warn("""This is only designed to work for cases where you have a single hole...
For multiple holes, or electrons, use the big table to work it out yourself!""")

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing function docstring and using warnings for user messaging.

The function hole_finder lacks a proper docstring that would explain parameters and return values. Additionally, using warnings for user instructions is not ideal as they might be suppressed.

Add a proper docstring and use print statements for user instructions:

 def hole_finder(vasprun_defect, bands_below=3, bands_above=3):
+    """
+    Identify a single hole in the electronic structure from a VASP calculation.
+    
+    Args:
+        vasprun_defect (str): Path to the vasprun.xml file
+        bands_below (int): Number of bands below VBM to include in analysis
+        bands_above (int): Number of bands above VBM to include in analysis
+        
+    Returns:
+        None: Results are printed to stdout
+    """
-    warnings.warn("""Python is 0-indexed, VASP in 1-indexed. All k-points and
-    bands shown as an output from this script are 1-indexed, i.e. VASP-friendly.""")
+    print("\nNOTE: Python is 0-indexed, VASP in 1-indexed. All k-points and")
+    print("bands shown as an output from this script are 1-indexed, i.e. VASP-friendly.\n")

-    warnings.warn("""This is only designed to work for cases where you have a single hole...
-    For multiple holes, or electrons, use the big table to work it out yourself!""")
+    print("NOTE: This is only designed to work for cases where you have a single hole...")
+    print("For multiple holes, or electrons, use the big table to work it out yourself!\n")
📝 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
def hole_finder(vasprun_defect, bands_below=3, bands_above=3):
warnings.warn("""Python is 0-indexed, VASP in 1-indexed. All k-points and
bands shown as an output from this script are 1-indexed, i.e. VASP-friendly.""")
warnings.warn("""This is only designed to work for cases where you have a single hole...
For multiple holes, or electrons, use the big table to work it out yourself!""")
def hole_finder(vasprun_defect, bands_below=3, bands_above=3):
"""
Identify a single hole in the electronic structure from a VASP calculation.
Args:
vasprun_defect (str): Path to the vasprun.xml file
bands_below (int): Number of bands below VBM to include in analysis
bands_above (int): Number of bands above VBM to include in analysis
Returns:
None: Results are printed to stdout
"""
print("\nNOTE: Python is 0-indexed, VASP is 1-indexed. All k-points and")
print("bands shown as an output from this script are 1-indexed, i.e. VASP-friendly.\n")
print("NOTE: This is only designed to work for cases where you have a single hole...")
print("For multiple holes, or electrons, use the big table to work it out yourself!\n")
# ... rest of implementation ...
🧰 Tools
🪛 Ruff (0.8.2)

9-9: Missing docstring in public function

(D103)

Comment on lines +423 to +424
"source": "The `hole-finder.py` script provided in the `ShakeNBreak` `docs` directory can be useful for identifying hole states in VASP calculations, for `PARCHG` analysis.",
"id": "61a8cec047463ee0"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add more details about the hole-finder.py script.

The notebook mentions the hole-finder.py script but provides no details about how to use it or what it does. Consider adding a brief example or description of its functionality.

-The `hole-finder.py` script provided in the `ShakeNBreak` `docs` directory can be useful for identifying hole states in VASP calculations, for `PARCHG` analysis.
+The `hole-finder.py` script provided in the `ShakeNBreak` `docs` directory can be useful for identifying hole states in VASP calculations, for `PARCHG` analysis. 
+
+For example, to identify hole states in a calculation:
+```
+python hole-finder.py path/to/vasprun.xml
+```
+
+This script analyzes the vasprun.xml file to find single-hole electronic states and provides recommended energy integration parameters for visualizing the hole state using PARCHG outputs.
📝 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
"source": "The `hole-finder.py` script provided in the `ShakeNBreak` `docs` directory can be useful for identifying hole states in VASP calculations, for `PARCHG` analysis.",
"id": "61a8cec047463ee0"
"source": "The `hole-finder.py` script provided in the `ShakeNBreak` `docs` directory can be useful for identifying hole states in VASP calculations, for `PARCHG` analysis.\n\nFor example, to identify hole states in a calculation:\n

Copy link

@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: 0

🧹 Nitpick comments (1)
tests/test_cli.py (1)

321-321: Commented assertion for INCAR equality check

The assertion checking that the generated INCAR matches the reference INCAR has been commented out. While this likely helps accommodate changes related to the newly introduced progress bars, removing this verification could potentially allow regressions in INCAR generation to go undetected.

Consider adding a comment explaining why this assertion was disabled or implementing a more flexible comparison that allows for specific differences while still verifying essential INCAR properties.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d92991 and 42fd57f.

📒 Files selected for processing (1)
  • tests/test_cli.py (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: build (macos-14, 3.12)
  • GitHub Check: build (macos-14, 3.11)
  • GitHub Check: build (macos-14, 3.10)
  • GitHub Check: build (ubuntu-latest, 3.12)
  • GitHub Check: build (ubuntu-latest, 3.11)
  • GitHub Check: build (ubuntu-latest, 3.10)
🔇 Additional comments (1)
tests/test_cli.py (1)

1099-1100: Good addition to properly handle tqdm progress bar output in tests

Setting mix_stderr=False in the CliRunner is an appropriate solution to prevent tqdm progress bar output (which writes to stderr) from interfering with test output assertions. This ensures more reliable testing with the newly added progress bars.

Copy link

@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 (4)
README.md (2)

94-94: Unnecessary escape in author name
The backslash in Y\. Liu is not needed and may render incorrectly; please replace with Y. Liu.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

94-94: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


101-101: Inline HTML comment in list
There's an HTML comment (<!-- though didn't cite properly SnB or doped code papers... -->) at the end of this list item. It may break Markdown list rendering. Consider moving this note out of the list or removing it.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

101-101: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

tests/test_distortions.py (2)

89-89: Fix line length and consider extracting path.

The line exceeds the 107-character limit and could be made more readable.

-        self.V_Si_HfSiO_interface_entry = loadfn(os.path.join(self.DATA_DIR, "v_Si_C1_O1.70_HfSiO_interface_+1.json"))
+        self.V_Si_HfSiO_interface_entry = loadfn(
+            os.path.join(self.DATA_DIR, "v_Si_C1_O1.70_HfSiO_interface_+1.json")
+        )
🧰 Tools
🪛 Ruff (0.11.9)

89-89: Line too long (118 > 107)

(E501)


436-455: Test logic is correct but has minor style issues.

The test appropriately verifies the fallback behavior when CrystalNN returns fewer than 2 neighbors, addressing the bug mentioned in #85. The assertions confirm correct structure handling and distance constraints.

Consider addressing these minor style improvements:

-        self.assertTrue(output["distorted_atoms"] == [[82, 'Si'], [177, 'O']])
+        self.assertEqual(output["distorted_atoms"], [[82, "Si"], [177, "O"]])
-        self.assertTrue(min_dist > 1.5)  # Si-O dimer bond length is larger than min_dist in orig structure
+        self.assertGreater(min_dist, 1.5)  # Si-O dimer bond length is larger than min_dist in orig structure
🧰 Tools
🪛 Ruff (0.11.9)

448-448: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


449-449: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


450-450: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


450-450: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


450-450: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


454-454: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f31c0d9 and ccdd4d4.

📒 Files selected for processing (12)
  • CHANGELOG.rst (1 hunks)
  • README.md (3 hunks)
  • docs/ShakeNBreak_Example_Workflow.ipynb (2 hunks)
  • docs/ShakeNBreak_Polaron_Workflow.ipynb (1 hunks)
  • docs/conf.py (2 hunks)
  • docs/docs_requirements.txt (1 hunks)
  • docs/index.rst (3 hunks)
  • setup.py (3 hunks)
  • shakenbreak/analysis.py (6 hunks)
  • shakenbreak/distortions.py (4 hunks)
  • shakenbreak/energy_lowering_distortions.py (7 hunks)
  • tests/test_distortions.py (4 hunks)
✅ Files skipped from review due to trivial changes (3)
  • docs/docs_requirements.txt
  • docs/conf.py
  • CHANGELOG.rst
🚧 Files skipped from review as they are similar to previous changes (7)
  • docs/ShakeNBreak_Example_Workflow.ipynb
  • setup.py
  • shakenbreak/analysis.py
  • docs/index.rst
  • shakenbreak/distortions.py
  • shakenbreak/energy_lowering_distortions.py
  • docs/ShakeNBreak_Polaron_Workflow.ipynb
🧰 Additional context used
🪛 Ruff (0.11.9)
tests/test_distortions.py

89-89: Line too long (118 > 107)

(E501)


132-132: Redefinition of unused test_distort_V_Cd from line 96

(F811)


133-133: First line should end with a period, question mark, or exclamation point

Add closing punctuation

(D415)


135-135: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


136-136: Local variable output is assigned to but never used

Remove assignment to unused variable output

(F841)


139-139: Line too long (109 > 107)

(E501)


142-142: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


448-448: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


449-449: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


450-450: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


450-450: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


450-450: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


454-454: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)

🪛 Pylint (3.3.7)
tests/test_distortions.py

[error] 132-132: method already defined line 96

(E0102)

🪛 markdownlint-cli2 (0.17.2)
README.md

89-89: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


90-90: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


91-91: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


93-93: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


94-94: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


95-95: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


96-96: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


97-97: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


98-98: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


99-99: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


100-100: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


101-101: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


102-102: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build (ubuntu-latest, 3.12)
  • GitHub Check: build (ubuntu-latest, 3.10)
  • GitHub Check: build (ubuntu-latest, 3.11)
🔇 Additional comments (6)
README.md (5)

28-28: Literature citation request added
The new line politely asks users to cite both the code and theory/method paper, which aligns with best practices for academic software documentation.


63-63: Python API documentation updated
The updated tutorial link and docs URL are accurate and consistent with the new ReadTheDocs location.


89-90: New 2025 study entries added
Entries for López et al. and Ogawa et al. are correctly formatted and appropriately placed at the top of the list.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

89-89: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


90-90: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


95-96: Study entries for Kavanagh and Huang
The formatting and citation links for the Kavanagh et al. and Huang et al. entries look correct.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

95-95: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


96-96: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


100-100: Additional 2025 study entries
The entries for Desai et al. and Gierlotka et al. are well formatted with valid DOIs and publication details.

Also applies to: 102-102

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

100-100: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

tests/test_distortions.py (1)

1-4: Documentation addition looks good.

The module-level docstring appropriately notes the relationship with other test modules.

Copy link

@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: 2

♻️ Duplicate comments (1)
docs/hole-finder.py (1)

70-72: Eigenvalue channel reference is correct for spin-up hole case.

The code correctly uses "up-eigenval" for the spin-up hole case, which matches the expected behavior since the hole is in the spin-up channel.

This issue was previously flagged but the current code actually has the correct implementation. The eigenvalue channel reference is appropriate for the spin-up hole case.

🧹 Nitpick comments (7)
README.md (4)

28-28: Consider highlighting the citation request with a callout.
The added reminder to cite the code and theory/method is important—using a Markdown blockquote or admonition (e.g., > **Note:** …) will make it more prominent.


89-90: Use consistent list bullet style.
The new entries use - for bullets, whereas the rest of the document follows *. Switching to * will align with the established Markdown style.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

89-89: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


90-90: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


94-96: Maintain uniform list formatting.
These list items are prefixed with -; please convert them to * for consistency with the rest of the lists.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

94-94: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


95-95: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


96-96: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


100-101: Normalize list marker usage.
Update the newly added entries to use * bullets to match the existing Markdown conventions.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

100-100: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


101-101: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

docs/hole-finder.py (3)

1-8: Add module docstring to document the script's purpose.

The script lacks a module-level docstring explaining its purpose and usage.

+"""
+Helper script for identifying hole states in VASP defect calculations.
+
+This script analyzes vasprun.xml files to locate a single hole in the electronic
+structure and provides eigenvalue integration ranges for PARCHG calculations.
+"""
 import itertools
🧰 Tools
🪛 Ruff (0.11.9)

1-1: Missing docstring in public module

(D100)


47-49: Use a more descriptive variable name for the DataFrame.

The generic variable name df could be more descriptive to improve code readability.

-df = pd.DataFrame(eigenvals,
+eigenval_df = pd.DataFrame(eigenvals,
                   columns=["band", "k-point", "up-eigenval", "down-eigenval", "up-occupancy",
                            "down-occupancy"])

Remember to update all subsequent references to use eigenval_df instead of df.

🧰 Tools
🪛 Ruff (0.11.9)

47-47: Avoid using the generic variable name df for DataFrames

(PD901)


55-59: Fix line length violations for better readability.

The lines exceed the recommended length limit and should be wrapped.

-print(
-    "Locating hole... Disregard if you are concerned with any other situation than a defect with a SINGLE HOLE\n")
-print(
-    "Information on EINT min and max can be ignored if you are using pymatgen to make your PARCHG file")
+print("Locating hole... Disregard if you are concerned with any other situation "
+      "than a defect with a SINGLE HOLE\n")
+print("Information on EINT min and max can be ignored if you are using pymatgen "
+      "to make your PARCHG file")
🧰 Tools
🪛 Ruff (0.11.9)

56-56: Line too long (118 > 107)

(E501)


58-58: Line too long (108 > 107)

(E501)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccdd4d4 and db0e02c.

📒 Files selected for processing (7)
  • .github/workflows/pip_install_test.yml (3 hunks)
  • .github/workflows/test.yml (2 hunks)
  • README.md (3 hunks)
  • docs/hole-finder.py (1 hunks)
  • docs/index.rst (3 hunks)
  • tests/test_cli.py (3 hunks)
  • tests/test_distortions.py (13 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/index.rst
  • tests/test_cli.py
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
README.md

89-89: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


90-90: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


91-91: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


93-93: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


94-94: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


95-95: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


96-96: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


97-97: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


98-98: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


99-99: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


100-100: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


101-101: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


102-102: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

🪛 Ruff (0.11.9)
docs/hole-finder.py

1-1: Missing docstring in public module

(D100)


10-10: Missing docstring in public function

(D103)


47-47: Avoid using the generic variable name df for DataFrames

(PD901)


56-56: Line too long (118 > 107)

(E501)


58-58: Line too long (108 > 107)

(E501)


63-63: Avoid equality comparisons to True; use if hole_spin_up.empty: for truth checks

Replace with hole_spin_up.empty

(E712)


63-63: Avoid equality comparisons to False; use if not hole_spin_down.empty: for false checks

Replace with not hole_spin_down.empty

(E712)


68-68: Avoid equality comparisons to True; use if hole_spin_down.empty: for truth checks

Replace with hole_spin_down.empty

(E712)


68-68: Avoid equality comparisons to False; use if not hole_spin_up.empty: for false checks

Replace with not hole_spin_up.empty

(E712)


73-73: Avoid equality comparisons to True; use if hole_spin_up.empty: for truth checks

Replace with hole_spin_up.empty

(E712)


73-73: Avoid equality comparisons to True; use if hole_spin_down.empty: for truth checks

Replace with hole_spin_down.empty

(E712)

tests/test_distortions.py

109-110: Explicitly concatenated string should be implicitly concatenated

(ISC003)


116-116: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


117-117: Local variable output is assigned to but never used

Remove assignment to unused variable output

(F841)


123-123: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


167-167: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


168-168: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


169-169: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


170-170: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


171-171: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


172-172: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


357-357: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


365-365: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


368-368: First line should end with a period, question mark, or exclamation point

Add closing punctuation

(D415)


372-372: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


373-373: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


374-374: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


382-382: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


400-400: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


408-408: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


422-422: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


423-423: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


424-424: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


428-428: Use a regular assert instead of unittest-style assertGreater

Replace assertGreater(...) with assert ...

(PT009)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: test (ubuntu-latest, 3.10)
  • GitHub Check: test (ubuntu-latest, 3.13)
🔇 Additional comments (10)
README.md (1)

63-63: Confirm updated API doc links.
The Python API tutorial and docs URLs have been revised—please verify these point to the correct, versioned landing pages and update any other occurrences accordingly.

tests/test_distortions.py (2)

1-3: LGTM: Good documentation addition.

The module docstring helpfully clarifies that most distortion functions are implicitly tested in test_input.


410-431: Excellent test for addressing issue #85.

This test properly validates the fallback behavior when CrystalNN returns insufficient neighbors, ensuring the distortion functions handle complex interface structures correctly. The assertions comprehensively verify:

  • Structure integrity
  • Correct neighbor count
  • Expected distorted atom indices
  • Minimum distance constraints
🧰 Tools
🪛 Ruff (0.11.9)

422-422: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


423-423: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


424-424: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


428-428: Use a regular assert instead of unittest-style assertGreater

Replace assertGreater(...) with assert ...

(PT009)

.github/workflows/pip_install_test.yml (3)

12-12: Good improvement: More descriptive job name.

Renaming from build to test better reflects the job's actual purpose.


21-21: Efficient testing strategy: Focus on boundary Python versions.

Testing only Python 3.10 and 3.13 (oldest and latest supported) is a good strategy for PyPI installation validation, reducing CI overhead while maintaining adequate coverage.


46-46: Good consolidation: Simplified test commands.

Running pytest on the entire tests directory with markers is cleaner than separate commands for individual test files, while maintaining the same test coverage.

Also applies to: 51-51

.github/workflows/test.yml (4)

11-11: Consistent improvement: Job name alignment.

Renaming to test maintains consistency with the pip install workflow and better describes the job function.


17-17: Strategic testing matrix: Boundary version focus.

Testing Python 3.10 and 3.13 provides good coverage while reducing CI complexity and execution time.


47-47: Improved test organization: Consolidated commands.

The consolidated pytest commands are cleaner and maintain the same functionality while reducing duplication.

Also applies to: 52-52


59-59: Excellent enhancement: Dynamic artifact naming.

Using matrix variables in the artifact name (SnB_GH_Actions_plots_${{ matrix.os }}_${{ matrix.python-version }}) makes it easy to distinguish test results across different OS and Python version combinations.

Comment on lines +63 to +74
if hole_spin_up.empty == True and hole_spin_down.empty == False:
print("Hole is in the spin down channel:\n", hole_spin_down)
print("Recommended min and max for EINT in PARCHG calculation:",
round(hole_spin_down["down-eigenval"].min(), 2),
round(hole_spin_down["down-eigenval"].max(), 2))
elif hole_spin_down.empty == True and hole_spin_up.empty == False:
print("Hole is in the spin up channel:\n", hole_spin_up)
print("Recommended min and max for EINT in PARCHG calculation:",
round(hole_spin_up["up-eigenval"].min(), 2),
round(hole_spin_up["up-eigenval"].max(), 2))
elif hole_spin_up.empty == True and hole_spin_down.empty == True:
print("No holes found!")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify boolean comparisons and fix logic consistency.

The boolean equality comparisons are unnecessarily verbose and should be simplified. Additionally, there's a potential logic issue where the elif conditions might not be mutually exclusive.

-if hole_spin_up.empty == True and hole_spin_down.empty == False:
+if hole_spin_up.empty and not hole_spin_down.empty:
     print("Hole is in the spin down channel:\n", hole_spin_down)
     print("Recommended min and max for EINT in PARCHG calculation:",
           round(hole_spin_down["down-eigenval"].min(), 2),
           round(hole_spin_down["down-eigenval"].max(), 2))
-elif hole_spin_down.empty == True and hole_spin_up.empty == False:
+elif hole_spin_down.empty and not hole_spin_up.empty:
     print("Hole is in the spin up channel:\n", hole_spin_up)
     print("Recommended min and max for EINT in PARCHG calculation:",
           round(hole_spin_up["up-eigenval"].min(), 2),
           round(hole_spin_up["up-eigenval"].max(), 2))
-elif hole_spin_up.empty == True and hole_spin_down.empty == True:
+elif hole_spin_up.empty and hole_spin_down.empty:
     print("No holes found!")
+else:
+    print("Multiple holes detected in both spin channels!")
📝 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
if hole_spin_up.empty == True and hole_spin_down.empty == False:
print("Hole is in the spin down channel:\n", hole_spin_down)
print("Recommended min and max for EINT in PARCHG calculation:",
round(hole_spin_down["down-eigenval"].min(), 2),
round(hole_spin_down["down-eigenval"].max(), 2))
elif hole_spin_down.empty == True and hole_spin_up.empty == False:
print("Hole is in the spin up channel:\n", hole_spin_up)
print("Recommended min and max for EINT in PARCHG calculation:",
round(hole_spin_up["up-eigenval"].min(), 2),
round(hole_spin_up["up-eigenval"].max(), 2))
elif hole_spin_up.empty == True and hole_spin_down.empty == True:
print("No holes found!")
if hole_spin_up.empty and not hole_spin_down.empty:
print("Hole is in the spin down channel:\n", hole_spin_down)
print("Recommended min and max for EINT in PARCHG calculation:",
round(hole_spin_down["down-eigenval"].min(), 2),
round(hole_spin_down["down-eigenval"].max(), 2))
elif hole_spin_down.empty and not hole_spin_up.empty:
print("Hole is in the spin up channel:\n", hole_spin_up)
print("Recommended min and max for EINT in PARCHG calculation:",
round(hole_spin_up["up-eigenval"].min(), 2),
round(hole_spin_up["up-eigenval"].max(), 2))
elif hole_spin_up.empty and hole_spin_down.empty:
print("No holes found!")
else:
print("Multiple holes detected in both spin channels!")
🧰 Tools
🪛 Ruff (0.11.9)

63-63: Avoid equality comparisons to True; use if hole_spin_up.empty: for truth checks

Replace with hole_spin_up.empty

(E712)


63-63: Avoid equality comparisons to False; use if not hole_spin_down.empty: for false checks

Replace with not hole_spin_down.empty

(E712)


68-68: Avoid equality comparisons to True; use if hole_spin_down.empty: for truth checks

Replace with hole_spin_down.empty

(E712)


68-68: Avoid equality comparisons to False; use if not hole_spin_up.empty: for false checks

Replace with not hole_spin_up.empty

(E712)


73-73: Avoid equality comparisons to True; use if hole_spin_up.empty: for truth checks

Replace with hole_spin_up.empty

(E712)


73-73: Avoid equality comparisons to True; use if hole_spin_down.empty: for truth checks

Replace with hole_spin_down.empty

(E712)

🤖 Prompt for AI Agents
In docs/hole-finder.py around lines 63 to 74, simplify the boolean checks by
removing explicit comparisons to True or False and use direct truthiness checks
instead. Also, revise the conditional logic to ensure the conditions are
mutually exclusive and cover all cases correctly, possibly by reordering or
combining conditions to avoid overlap and improve clarity.

Copy link

@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

♻️ Duplicate comments (1)
tests/test_distortions.py (1)

114-123: Fix unused variable in test method.

The test logic correctly validates negative distortion factor handling, but there's an unused variable that should be removed.

Apply this diff to fix the unused variable:

     def test_distort_negative_factor(self):
         vac_coords = np.array([0, 0, 0])  # Cd vacancy fractional coordinates
         with self.assertRaises(ValueError) as e:
-            output = distortions.distort(self.V_Cd_struc, 2, -0.5, frac_coords=vac_coords)
+            distortions.distort(self.V_Cd_struc, 2, -0.5, frac_coords=vac_coords)
🧰 Tools
🪛 Ruff (0.11.9)

116-116: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


117-117: Local variable output is assigned to but never used

Remove assignment to unused variable output

(F841)


118-118: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)

🧹 Nitpick comments (5)
tests/test_analysis.py (5)

51-52: Use os.path.join for nested paths instead of embedding '/'
Embedding directory separators inside a single join argument can break Windows compatibility.

Refactor the call to:

- Structure.from_file(
-     os.path.join(self.VASP_CDTE_DATA_DIR, "vac_1_Cd_0/Bond_Distortion_-30.0%/CONTCAR")
- )
+ Structure.from_file(
+     os.path.join(
+         self.VASP_CDTE_DATA_DIR,
+         "vac_1_Cd_0",
+         "Bond_Distortion_-30.0%",
+         "CONTCAR",
+     )
+ )

222-223: Combine implicit string literals into a single literal
Avoid implicit concatenation for improved readability and to satisfy linters.

- "Local order parameters (i.e. resemblance to given " "structural motif, via CrystalNN):"
+ "Local order parameters (i.e. resemblance to given structural motif, via CrystalNN):"
🧰 Tools
🪛 Ruff (0.11.9)

222-222: Implicitly concatenated string literals on one line

Combine string literals

(ISC001)


585-586: Merge adjacent string literals into one
Clean up the assertion message by removing the redundant split.

- mock_print.assert_called_with("Comparing structures to -40.0% bond distorted " "structure...")
+ mock_print.assert_called_with("Comparing structures to -40.0% bond distorted structure...")
🧰 Tools
🪛 Ruff (0.11.9)

585-585: Implicitly concatenated string literals on one line

Combine string literals

(ISC001)


680-681: Consolidate concatenated literals in warning message
Simplify the combined string into a single literal.

- "All structures in defect_structures_dict are not converged. " "Returning None."
+ "All structures in defect_structures_dict are not converged. Returning None."
🧰 Tools
🪛 Ruff (0.11.9)

680-680: Implicitly concatenated string literals on one line

Combine string literals

(ISC001)


985-986: Remove extraneous f prefix on string without placeholders
The warning check string doesn’t use interpolation, so the f prefix is unnecessary.

- any(f"OUTCAR(.gz) file not found in path" in str(warning.message) for warning in w)
+ any("OUTCAR(.gz) file not found in path" in str(warning.message) for warning in w)
🧰 Tools
🪛 Ruff (0.11.9)

985-985: f-string without any placeholders

Remove extraneous f prefix

(F541)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db0e02c and cef4629.

📒 Files selected for processing (4)
  • shakenbreak/analysis.py (7 hunks)
  • tests/test_analysis.py (34 hunks)
  • tests/test_distortions.py (14 hunks)
  • tests/test_input.py (16 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • shakenbreak/analysis.py
🧰 Additional context used
🪛 Ruff (0.11.9)
tests/test_analysis.py

94-94: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


95-95: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


96-96: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


97-97: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


98-98: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


102-102: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


103-103: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


104-104: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


109-109: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


190-190: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)


199-199: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)


216-216: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


222-222: Implicitly concatenated string literals on one line

Combine string literals

(ISC001)


263-263: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


313-313: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


313-313: Consider [*bond_distortions, "Unperturbed"] instead of concatenation

Replace with [*bond_distortions, "Unperturbed"]

(RUF005)


328-328: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


343-343: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


346-346: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


348-348: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


358-358: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


360-360: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


360-360: Consider [*bond_distortions, "Unperturbed"] instead of concatenation

Replace with [*bond_distortions, "Unperturbed"]

(RUF005)


381-381: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


397-397: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


399-399: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


409-409: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


410-410: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


433-433: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)


434-434: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


435-435: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


438-438: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


440-440: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


446-446: Use a regular assert instead of unittest-style assertFalse

Replace assertFalse(...) with assert ...

(PT009)


460-460: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


461-461: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


468-468: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


469-469: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


495-495: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


496-496: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


497-497: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


515-515: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


519-519: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


525-525: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


532-532: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


534-534: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


540-540: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


544-544: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


576-576: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


577-577: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


578-578: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


585-585: Implicitly concatenated string literals on one line

Combine string literals

(ISC001)


587-587: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


588-588: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


589-589: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


600-600: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


601-601: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


602-602: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


614-614: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


615-615: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


642-642: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


643-643: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


646-646: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


650-650: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


654-654: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


658-658: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


664-664: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


666-666: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


680-680: Implicitly concatenated string literals on one line

Combine string literals

(ISC001)


682-682: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)


683-683: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


684-684: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


685-685: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


706-706: Avoid using the generic variable name df for DataFrames

(PD901)


712-712: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


757-757: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


985-985: f-string without any placeholders

Remove extraneous f prefix

(F541)

tests/test_distortions.py

109-110: Explicitly concatenated string should be implicitly concatenated

(ISC003)


116-116: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


117-117: Local variable output is assigned to but never used

Remove assignment to unused variable output

(F841)


118-118: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


167-167: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


168-168: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


169-169: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


170-170: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


171-171: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


172-172: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


234-234: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


357-357: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


365-365: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


368-368: First line should end with a period, question mark, or exclamation point

Add closing punctuation

(D415)


372-372: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


373-373: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


374-374: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


382-382: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


400-400: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


408-408: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


422-422: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


423-423: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


424-424: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


428-428: Use a regular assert instead of unittest-style assertGreater

Replace assertGreater(...) with assert ...

(PT009)

tests/test_input.py

1573-1573: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


1588-1588: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


1691-1691: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


1704-1704: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


2268-2268: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


2270-2270: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


2273-2273: Use a regular assert instead of unittest-style assertEqual

(PT009)


2278-2278: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


2623-2623: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


2731-2731: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


2797-2797: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


3638-3638: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


3752-3752: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: test (ubuntu-latest, 3.13)
  • GitHub Check: test (ubuntu-latest, 3.10)
🔇 Additional comments (13)
tests/test_input.py (8)

1153-1154: LGTM: Helpful clarification about NELECT parameter behavior

This comment update provides valuable context about the NELECT parameter handling differences between doped versions, which helps explain why manual updating isn't needed in these tests.


1690-1692: LGTM: Variable naming improvement for clarity

The renaming from Int_Cd_2_Bond_Distortion_folder to Int_Cd_2_0_Bond_Distortion_folder consistently adds the charge state (_0) to the variable names, making the test code more explicit and readable.

Also applies to: 1703-1703, 1708-1708, 1715-1715

🧰 Tools
🪛 Ruff (0.11.9)

1691-1691: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


2025-2027: LGTM: Informative comment about INCAR parameter differences

The comment clearly explains why NELECT and NUPDOWN parameters differ between the defect types, providing helpful context for understanding the test expectations.


2214-2214: Minor: Unicode angstrom symbol change

The change from uppercase Å (U+212B) to lowercase å (U+212b) in the angstrom symbol is noted in the summary but appears to be a very minor typographical change.


2267-2269: LGTM: Consistent variable naming with charge state

These changes maintain consistency with the earlier variable renaming pattern, explicitly including the charge state in the variable names for better clarity.

Also applies to: 2277-2277, 2282-2282, 2289-2289

🧰 Tools
🪛 Ruff (0.11.9)

2268-2268: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


2730-2732: LGTM: Consistent folder variable naming

The renaming continues the pattern of adding charge state information to variable names, maintaining consistency throughout the test file.

🧰 Tools
🪛 Ruff (0.11.9)

2731-2731: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)


2797-2801: LGTM: Correct assertion usage for exception testing

These assertIn calls have the correct argument order, checking if the expected error message is contained within the exception string.

Also applies to: 3752-3755

🧰 Tools
🪛 Ruff (0.11.9)

2797-2797: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)


3637-3639: LGTM: Final consistent variable naming update

This completes the consistent pattern of adding charge state information to folder variable names throughout the test file.

🧰 Tools
🪛 Ruff (0.11.9)

3638-3638: Use a regular assert instead of unittest-style assertTrue

Replace assertTrue(...) with assert ...

(PT009)

tests/test_distortions.py (4)

1-3: LGTM!

The module docstring provides helpful context about the implicit testing relationship with test_input.


73-75: LGTM!

Good addition of the HfSiO interface defect entry to support testing of complex interface structures.


234-238: LGTM!

Good fix to properly compare the exception message string instead of the exception object.

🧰 Tools
🪛 Ruff (0.11.9)

234-234: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


410-431: Excellent test coverage for the fallback behavior!

This test effectively validates the fix for issue #85, ensuring correct behavior when CrystalNN returns fewer than 2 neighbors for interface structures. The test properly:

  • Uses a complex interface structure that triggers the fallback behavior
  • Validates the expected number of distorted neighbors (2)
  • Checks the specific atoms that get distorted
  • Ensures the minimum interatomic distance constraint is satisfied

The test directly addresses the bug where insufficient neighbors from CrystalNN would cause failures in complex structures.

🧰 Tools
🪛 Ruff (0.11.9)

422-422: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


423-423: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


424-424: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


428-428: Use a regular assert instead of unittest-style assertGreater

Replace assertGreater(...) with assert ...

(PT009)

tests/test_analysis.py (1)

94-97: All other changes are purely cosmetic or formatting-only (condensed assertions, style tweaks, added test cases without logic changes). No functional issues detected.

Also applies to: 102-104, 112-112, 141-141, 151-151, 181-181, 190-190, 199-199, 215-216, 224-224, 248-248, 262-263, 283-283, 288-288, 313-313, 328-328, 343-343, 347-348, 353-353, 381-381, 383-384, 397-399, 409-410, 433-434, 438-440, 448-449, 458-458, 461-461, 468-470, 479-479, 500-500, 509-509, 514-515, 519-523, 531-533, 541-544, 566-567, 576-578, 587-589, 598-600, 614-615, 641-642, 649-652, 655-658, 664-669, 677-684, 689-689, 705-706, 712-712, 715-716, 723-726, 735-737, 751-757, 783-785, 808-808, 824-826, 836-838, 891-892, 949-950

🧰 Tools
🪛 Ruff (0.11.9)

94-94: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


95-95: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


96-96: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)


97-97: Use a regular assert instead of unittest-style assertEqual

Replace assertEqual(...) with assert ...

(PT009)

Comment on lines +2623 to +2627
self.assertIn(
str(e.exception),
"Input `defect_entries` dict matches `doped`/`PyCDT` format, but no 'bulk' entry present. "
"Please try again providing a `bulk` entry in `defect_entries`.",
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect assertion argument order

The assertIn method call has its arguments in the wrong order. Currently it's checking if the exception string contains the error message, but it should check if the error message is contained in the exception string.

Apply this fix:

-        self.assertIn(
-            str(e.exception),
-            "Input `defect_entries` dict matches `doped`/`PyCDT` format, but no 'bulk' entry present. "
-            "Please try again providing a `bulk` entry in `defect_entries`.",
-        )
+        self.assertIn(
+            "Input `defect_entries` dict matches `doped`/`PyCDT` format, but no 'bulk' entry present. "
+            "Please try again providing a `bulk` entry in `defect_entries`.",
+            str(e.exception),
+        )
📝 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
self.assertIn(
str(e.exception),
"Input `defect_entries` dict matches `doped`/`PyCDT` format, but no 'bulk' entry present. "
"Please try again providing a `bulk` entry in `defect_entries`.",
)
self.assertIn(
"Input `defect_entries` dict matches `doped`/`PyCDT` format, but no 'bulk' entry present. "
"Please try again providing a `bulk` entry in `defect_entries`.",
str(e.exception),
)
🧰 Tools
🪛 Ruff (0.11.9)

2623-2623: Use a regular assert instead of unittest-style assertIn

Replace assertIn(...) with assert ...

(PT009)

🤖 Prompt for AI Agents
In tests/test_input.py around lines 2623 to 2627, the assertIn call has its
arguments reversed; it currently checks if the exception string contains the
error message, but it should verify if the expected error message is contained
within the exception string. Swap the arguments so that the expected error
message string is the first argument and the exception string is the second
argument in the assertIn call.

@kavanase kavanase merged commit 30ba2e8 into main Jun 9, 2025
6 checks passed
This was referenced Oct 10, 2025
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.

How to generate distortions in complex strucures?

2 participants