Skip to content

Conversation

@jan-janssen
Copy link
Member

@jan-janssen jan-janssen commented Nov 1, 2025

Summary by CodeRabbit

  • Chores
    • Updated SciPy dependency from version 1.9.3 to 1.15.0 in the conda environment.
    • Updated spherical harmonics computation implementation to align with current scipy library conventions.

@coderabbitai
Copy link

coderabbitai bot commented Nov 1, 2025

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between 3897212 and 76dadf4.

📒 Files selected for processing (1)
  • structuretoolkit/analyse/neighbors.py (3 hunks)
 _____________________________________________________________
< Oompa Loompa doompadee doo, I've got a code review for you. >
 -------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

Tip

CodeRabbit can suggest fixes for GitHub Check annotations.

Configure reviews.tools.github-checks in your project's settings in CodeRabbit to adjust the time to wait for GitHub Checks to complete.

Walkthrough

Updated minimum Python version to 3.10, replaced SciPy 1.9.3 with 1.15.0, and modernized type hints across 20+ modules from Optional[T]/Union[A, B] to PEP 604 syntax (T | None, A | B). Updated CI pipeline and Python classifiers accordingly.

Changes

Cohort / File(s) Summary
Environment & Dependency Configuration
.ci_support/environment-old.yml, .github/workflows/pipeline.yml, pyproject.toml
Updated SciPy dependency from 1.9.3 to 1.15.0; bumped minimum Python requirement from 3.9 to 3.10; updated CI pipeline to use Python 3.10; added Python 3.13 classifier and removed Python 3.9 classifier
Type Hint Modernization - Analyse Module
structuretoolkit/analyse/distance.py, structuretoolkit/analyse/dscribe.py, structuretoolkit/analyse/neighbors.py, structuretoolkit/analyse/pyscal.py, structuretoolkit/analyse/snap.py, structuretoolkit/analyse/spatial.py, structuretoolkit/analyse/strain.py, structuretoolkit/analyse/symmetry.py
Replaced Optional[T] with `T
Type Hint Modernization - Build Module
structuretoolkit/build/aimsgb.py, structuretoolkit/build/compound.py, structuretoolkit/build/sqs.py, structuretoolkit/build/surface.py
Replaced Optional[T] with `T
Type Hint Modernization - Common & Visualization
structuretoolkit/common/helper.py, structuretoolkit/visualize.py
Replaced Optional[T] with `T

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Areas requiring extra attention:
    • structuretoolkit/analyse/neighbors.py: Verify sph_harm to sph_harm_y function call correctness and parameter order (sph_harm_y(l, m, phi, theta) vs prior order)
    • Dependency version bumps: Confirm SciPy 1.15.0 compatibility with existing codebase and Python 3.10+ minimum compatibility
    • Complex signature changes in structuretoolkit/build/sqs.py with multi-parameter unions to ensure type correctness

Possibly related PRs

  • Update scipy to 1.15.1 #306: Both modify SciPy version pins in dependency files (bumping to 1.15.0)
  • Update scipy to 1.14.0 #202: Both update CI/environment Python and SciPy versions (Python 3.9 removal, SciPy updates)
  • extend ruff linter #315: Both modify overlapping code areas in structuretoolkit/analyse/dscribe.py and structuretoolkit/analyse/neighbors.py with similar signature/type-hint changes

Poem

🐰 Hops through types so clean and bright,
From Optional to union's light,
Python 3.10 takes the stage,
Modernized hints fill every page,
Backwards leaps for SciPy's way!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Rename sph_harm to sph_harm_y" refers to a real and specific change present in the changeset—the import and usage of sph_harm_y in structuretoolkit/analyse/neighbors.py is indeed part of this PR. However, this rename is a minor change relative to the primary work. The overwhelming majority of changes throughout the codebase involve modernizing type annotations from Optional[T] and Union[...] syntax to PEP 604 union syntax (T | None, A | B) across at least 17 files, which represents the substantive effort in this changeset. The title fails to capture this main objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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.

@codecov
Copy link

codecov bot commented Nov 1, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.78%. Comparing base (1c1fc14) to head (76dadf4).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #413   +/-   ##
=======================================
  Coverage   82.78%   82.78%           
=======================================
  Files          24       24           
  Lines        1807     1807           
=======================================
  Hits         1496     1496           
  Misses        311      311           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Caution

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

⚠️ Outside diff range comments (2)
structuretoolkit/build/sqs.py (2)

128-143: Update the docstring to match the modernized type hint.

The function signature now uses PEP 604 syntax (Atoms | np.ndarray), but the docstring at line 135 still references the old Union syntax. Update the docstring for consistency.

Apply this diff to update the docstring:

     """
     Remap the results of SQS optimization.
 
     Args:
-        result (Dict[str, Union[Atoms, np.ndarray]]): The result of SQS optimization.
+        result (dict[str, Atoms | np.ndarray]): The result of SQS optimization.
 
     Returns:
-        Tuple[Atoms, Dict[str, list]]: The remapped structure and short-range order parameters.
+        tuple[Atoms, dict[str, list]]: The remapped structure and short-range order parameters.
 
     """

160-242: Update the docstring to match the modernized type hints.

The function signature uses PEP 604 syntax throughout (e.g., float | int, dict[int, float] | None), but the docstring at lines 184-202 still uses the old Optional and Union syntax. Update the docstring for consistency.

Apply this diff to update the docstring:

     """
     Generate SQS structures.
 
     Args:
         structure (Atoms): The initial structure.
-        mole_fractions (Dict[str, Union[float, int]]): The mole fractions of each species.
-        weights (Optional[Dict[int, float]]): The weights for each shell.
-        objective (Union[float, np.ndarray]): The target objective value.
-        iterations (Union[float, int]): The number of iterations.
+        mole_fractions (dict[str, float | int]): The mole fractions of each species.
+        weights (dict[int, float] | None): The weights for each shell.
+        objective (float | np.ndarray): The target objective value.
+        iterations (float | int): The number of iterations.
         output_structures (int): The number of output structures.
         mode (str): The mode for selecting configurations.
-        num_threads (Optional[int]): The number of threads to use.
-        prefactors (Optional[Union[float, np.ndarray]]): The prefactors for each shell.
-        pair_weights (Optional[np.ndarray]): The pair weights.
-        rtol (Optional[float]): The relative tolerance.
-        atol (Optional[float]): The absolute tolerance.
-        which (Optional[Iterable[int]]): The indices of the shells to optimize.
-        shell_distances (Optional[Iterable[int]]): The distances for each shell.
-        minimal (Optional[bool]): Whether to minimize the objective function.
-        similar (Optional[bool]): Whether to generate similar structures.
-        return_statistics (Optional[bool]): Whether to return additional statistics.
+        num_threads (int | None): The number of threads to use.
+        prefactors (float | np.ndarray | None): The prefactors for each shell.
+        pair_weights (np.ndarray | None): The pair weights.
+        rtol (float | None): The relative tolerance.
+        atol (float | None): The absolute tolerance.
+        which (Iterable[int] | None): The indices of the shells to optimize.
+        shell_distances (Iterable[int] | None): The distances for each shell.
+        minimal (bool | None): Whether to minimize the objective function.
+        similar (bool | None): Whether to generate similar structures.
+        return_statistics (bool | None): Whether to return additional statistics.
 
     Returns:
-        Union[Atoms, Tuple[Atoms, Dict[str, list], int, float]]: The generated structures or a tuple containing the structures, short-range order parameters breakdown, number of iterations, and average cycle time.
+        Atoms | tuple[Atoms, dict[str, list], int, float]: The generated structures or a tuple containing the structures, short-range order parameters breakdown, number of iterations, and average cycle time.
 
     """
🧹 Nitpick comments (1)
structuretoolkit/visualize.py (1)

44-44: Consider refactoring mutable default arguments.

Static analysis correctly flags the use of np.array(...) in default arguments. While this specific case is low-risk (the arrays aren't mutated), it's a best practice to use None as the default and create the array inside the function.

Apply this pattern:

 def plot3d(
     ...
-    view_plane: np.ndarray = np.array([0, 0, 1]),
+    view_plane: np.ndarray | None = None,
     ...
 ):
+    if view_plane is None:
+        view_plane = np.array([0, 0, 1])

Note: This is a pre-existing issue and not critical for this PR focused on type hint modernization.

Also applies to: 211-211

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9edbd88 and 3897212.

📒 Files selected for processing (17)
  • .ci_support/environment-old.yml (1 hunks)
  • .github/workflows/pipeline.yml (1 hunks)
  • pyproject.toml (1 hunks)
  • structuretoolkit/analyse/distance.py (1 hunks)
  • structuretoolkit/analyse/dscribe.py (1 hunks)
  • structuretoolkit/analyse/neighbors.py (21 hunks)
  • structuretoolkit/analyse/pyscal.py (4 hunks)
  • structuretoolkit/analyse/snap.py (3 hunks)
  • structuretoolkit/analyse/spatial.py (6 hunks)
  • structuretoolkit/analyse/strain.py (4 hunks)
  • structuretoolkit/analyse/symmetry.py (2 hunks)
  • structuretoolkit/build/aimsgb.py (1 hunks)
  • structuretoolkit/build/compound.py (5 hunks)
  • structuretoolkit/build/sqs.py (2 hunks)
  • structuretoolkit/build/surface.py (2 hunks)
  • structuretoolkit/common/helper.py (2 hunks)
  • structuretoolkit/visualize.py (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
structuretoolkit/analyse/neighbors.py (2)
structuretoolkit/analyse/spatial.py (1)
  • positions (331-341)
structuretoolkit/analyse/strain.py (1)
  • num_neighbors (60-64)
🪛 Ruff (0.14.2)
structuretoolkit/analyse/dscribe.py

16-16: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)

structuretoolkit/visualize.py

44-44: Do not perform function call np.array in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


211-211: Do not perform function call np.array in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: unittest_matrix (windows-latest, 3.13, win-64-py-3-13)
🔇 Additional comments (23)
structuretoolkit/build/aimsgb.py (1)

61-61: LGTM! Type annotation modernized correctly.

The type hint has been updated to use PEP 604 syntax (float | None), which is the preferred style for Python 3.10+. The parameter's deprecation handling remains intact.

pyproject.toml (1)

14-25: LGTM! Python version requirements correctly updated.

The minimum Python version has been appropriately raised to 3.10, and the classifiers have been updated to reflect support for Python 3.10-3.13. This aligns well with the type hint modernization throughout the codebase.

.github/workflows/pipeline.yml (1)

152-152: LGTM! CI configuration aligned with new Python minimum.

The unittest_old workflow now correctly tests against Python 3.10, matching the updated minimum version requirement in pyproject.toml.

structuretoolkit/analyse/distance.py (1)

7-8: LGTM! Type hints modernized to PEP 604 syntax.

The update from Optional[np.ndarray] to np.ndarray | None is correct and aligns with Python 3.10+ best practices. The change is purely syntactic with no runtime impact.

structuretoolkit/common/helper.py (2)

25-25: LGTM! Type annotation modernized.

The update to np.ndarray | None correctly modernizes the type hint to Python 3.10+ syntax.


277-277: LGTM! Union type modernized to PEP 604 syntax.

The conversion from Union[Atoms, list, tuple, np.ndarray, float] to Atoms | list | tuple | np.ndarray | float is correct and more readable.

structuretoolkit/build/surface.py (2)

13-16: LGTM! Type hints consistently modernized.

All four optional list parameters have been correctly updated to use list | None syntax, maintaining semantic equivalence while adopting Python 3.10+ conventions.


100-103: LGTM! Matching type hint updates.

The parameter type hints in high_index_surface correctly mirror the modernization applied to get_high_index_surface_info.

structuretoolkit/analyse/dscribe.py (1)

7-12: LGTM! Type hints modernized correctly.

The parameter type hints have been properly updated to use PEP 604 union syntax (float | None, int | None, np.ndarray | None), consistent with Python 3.10+ conventions.

Also applies to: 15-15, 19-19

structuretoolkit/analyse/pyscal.py (4)

4-4: LGTM! Import statement cleaned up.

The removal of Optional from the import is appropriate since the file now uses PEP 604 union syntax throughout. Keeping Any for the return type on line 244 is correct.


27-30: LGTM! Parameter and return type hints modernized.

The function signature has been properly updated to use int | None, tuple | None for parameters and tuple[np.ndarray] | tuple[np.ndarray, np.ndarray] for the return type, all following PEP 604 conventions.


83-83: LGTM! Return type correctly updated.

The union return type dict[str, int] | np.ndarray properly reflects the function's behavior and uses modern syntax.


244-244: LGTM! Return type modernized.

The return type int | Any correctly uses PEP 604 syntax while maintaining the flexibility needed for the conditional return behavior.

.ci_support/environment-old.yml (1)

14-14: SciPy 1.15.0 compatibility verified—no breaking changes affecting identified imports.

Analysis of identified scipy imports against SciPy 1.15.0 release notes and deprecation history (1.9.3→1.15.0):

All six scipy functions/classes actively used in the codebase remain unchanged and available:

  • scipy.interpolate.interp1d
  • scipy.sparse.coo_matrix
  • scipy.spatial.transform.Rotation
  • scipy.spatial.cKDTree
  • scipy.spatial.{ConvexHull, Delaunay, Voronoi}
  • scipy.special.{gamma, sph_harm_y}
  • scipy.constants.physical_constants

None of these functions appear in SciPy's breaking changes or removal lists for versions 1.9.3 through 1.15.0. The breaking changes in 1.15.0 (BSpline.integrate return type, stats.wilcoxon method handling) and the removed/deprecated APIs (integrate.quadrature/romberg, signal functions, sparse-array legacy methods) do not affect the codebase's usage.

structuretoolkit/build/compound.py (1)

9-9: LGTM! Type hint modernization is correct.

The updates from Optional[float] to float | None follow PEP 604 union syntax introduced in Python 3.10. The changes are consistent across all function signatures and don't alter runtime behavior.

Also applies to: 34-34, 84-84, 126-126, 187-187

structuretoolkit/analyse/symmetry.py (1)

265-265: LGTM! Consistent type hint updates.

The method signatures correctly use bool | None instead of Optional[bool], aligning with Python 3.10+ union syntax.

Also applies to: 382-383

structuretoolkit/analyse/spatial.py (2)

4-4: Good modernization of Callable import.

Importing Callable from collections.abc instead of typing is the recommended approach as of Python 3.9+, and typing.Callable is deprecated for removal in Python 3.14.


228-228: LGTM! Type hints correctly modernized.

All nullable parameters now use PEP 604 union syntax consistently across the spatial analysis module.

Also applies to: 297-297, 410-410, 460-460, 693-695

structuretoolkit/analyse/strain.py (1)

31-31: LGTM! Type annotations properly updated.

The strain analysis methods now use modern union syntax for optional parameters. Changes are consistent and correct.

Also applies to: 86-86, 147-147, 320-320

structuretoolkit/visualize.py (1)

5-5: LGTM! Visualization module type hints modernized.

All optional parameters across the plotting functions now use T | None syntax. The changes are extensive but consistent and correct.

Also applies to: 33-42, 47-47, 207-209, 214-214, 287-296, 579-580, 729-730

structuretoolkit/analyse/neighbors.py (2)

151-152: LGTM! Type hint modernization is correct.

All type hints have been properly updated to use Python 3.10+ union syntax (T | None instead of Optional[T]). The changes are consistent throughout the neighbors module and don't affect runtime behavior.

Also applies to: 260-261, 280-282, 364-367, 433-437, 472-475, 517-520, 553-556, 649-650, 869-874, 949-954, 1010-1013, 1107-1113, 1152-1159, 1225-1227, 1282-1287, 1359-1368, 1398-1407


12-12: The main premise is incorrect—the code correctly implements the sph_harm_y migration despite confusing variable naming.

The parameter mapping is actually correct: theta (azimuthal) and phi (polar) are swapped in the function call to match sph_harm_y's expected signature (n, m, theta_polar, phi_azimuth). The code compensates for the opposite variable naming convention.

However, valid ancillary concerns remain:

  • No test coverage: Zero tests found for get_spherical_harmonics() or spherical harmonic calculations. This should be addressed.
  • Variable naming confusion: While functionally correct, naming theta for azimuthal angle and phi for polar angle is opposite to what sph_harm_y expects, making the code harder to verify. Consider renaming variables for clarity (e.g., azimuth/polar_angle) or adding an explanatory comment.
  • SciPy version: scipy==1.16.3 is pinned; sph_harm_y is available since scipy 1.15, so compatibility is not an issue.

The migration itself is sound from a scientific standpoint—results should be equivalent to a correct sph_harm implementation when parameters are properly mapped.

Likely an incorrect or invalid review comment.

structuretoolkit/analyse/snap.py (1)

72-72: LGTM! Type hints correctly modernized.

The weights parameter type hints now use PEP 604 union syntax, correctly expressing that the parameter accepts list, np.ndarray, or None.

Also applies to: 123-123, 661-661

weighting: Optional[np.ndarray] = None,
weighting: np.ndarray | None = None,
average: str = "off",
compression: dict = None,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix incomplete type hint modernization.

Line 14 was missed during the type hint modernization. The compression parameter should use the same PEP 604 syntax as other parameters in this function.

Apply this diff to complete the modernization:

-    compression: dict = None,
+    compression: dict | None = None,
📝 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
compression: dict = None,
compression: dict | None = None,
🤖 Prompt for AI Agents
In structuretoolkit/analyse/dscribe.py around line 14, the type hint for the
parameter `compression` still uses the old Optional style (`dict = None`);
update it to PEP 604 union syntax consistent with the rest of the function by
changing the annotation to `dict | None` (and keep the default `= None`) so the
parameter reads `compression: dict | None = None`.

@jan-janssen jan-janssen merged commit 1d8136f into main Nov 1, 2025
17 of 18 checks passed
@jan-janssen jan-janssen deleted the sph_harm branch November 1, 2025 14:56
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.

2 participants