Skip to content

Recreate structure fresh during get_primitive_cell#144

Open
pmrv wants to merge 10 commits intomainfrom
standardize
Open

Recreate structure fresh during get_primitive_cell#144
pmrv wants to merge 10 commits intomainfrom
standardize

Conversation

@pmrv
Copy link
Copy Markdown
Contributor

@pmrv pmrv commented Feb 14, 2024

This avoids a problem, when you pass standardize=True and the resulting cell would be larger than the original.

Summary by CodeRabbit

  • Bug Fixes

    • Primitive-cell extraction now rejects non-fully periodic inputs, always returns explicitly periodic primitives, and warns if custom per-atom arrays aren’t preserved.
  • Documentation

    • Expanded docstring warning to explain non-copied custom arrays and periodicity requirements.
  • Tests

    • Broadened and strengthened symmetry test coverage, updated tests to use periodic boundary conditions, and removed an obsolete symmetry test module.

@pmrv pmrv added bug Something isn't working enhancement New feature or request labels Feb 14, 2024
@pmrv pmrv requested a review from samwaseda February 14, 2024 17:01
@jan-janssen
Copy link
Copy Markdown
Member

======================================================================
ERROR: test_get_primitive_cell (test_symmetry.TestAtoms)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/work/structuretoolkit/structuretoolkit/tests/test_symmetry.py", line 149, in test_get_primitive_cell
    self.assertEqual(len(basis), len(sym.get_primitive_cell(standardize=True)))
  File "/home/runner/work/structuretoolkit/structuretoolkit/structuretoolkit/analyse/symmetry.py", line 349, in get_primitive_cell
    raise ValueError("Can only symmetrize periodic structures.")
ValueError: Can only symmetrize periodic structures.

======================================================================
ERROR: test_get_primitive_cell_hex (test_symmetry.TestAtoms)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/work/structuretoolkit/structuretoolkit/tests/test_symmetry.py", line 175, in test_get_primitive_cell_hex
    structure_prim_base = sym.get_primitive_cell()
  File "/home/runner/work/structuretoolkit/structuretoolkit/structuretoolkit/analyse/symmetry.py", line 349, in get_primitive_cell
    raise ValueError("Can only symmetrize periodic structures.")
ValueError: Can only symmetrize periodic structures.

----------------------------------------------------------------------

I mark this pull request as draft until the tests pass.

@jan-janssen jan-janssen marked this pull request as draft February 14, 2024 17:14
@pmrv pmrv marked this pull request as ready for review February 14, 2024 17:26
@pmrv
Copy link
Copy Markdown
Contributor Author

pmrv commented Feb 14, 2024

I made tests pass by passing pbc=True, I think this was intended anyway.

@pmrv pmrv requested a review from samwaseda February 15, 2024 13:41
@jan-janssen jan-janssen self-requested a review as a code owner July 18, 2024 06:34
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jul 18, 2024

📝 Walkthrough

Walkthrough

get_primitive_cell now requires fully-periodic inputs, uses spglib’s scaled positions directly to build a new primitive Structure with pbc=[True, True, True], warns if custom ASE arrays are not preserved, expands the docstring warning, expands symmetry-related tests, and removes an older unittest-based test module.

Changes

Cohort / File(s) Summary
Core symmetry logic
src/structuretoolkit/analyse/symmetry.py
Refuse non-fully-periodic inputs, adapt to spglib returning scaled positions, construct new type(self._structure) from symbols, scaled_positions, and cell with forced pbc=[True, True, True], detect custom ASE arrays on the original structure and emit warnings.warn, and update docstring warning text.
Expanded/updated tests
tests/test_analyse_symmetry.py
Broadened error expectations to accept SpglibError or stk.common.SymmetryError; added many tests for equivalent points, get_symmetry outputs, vector/tensor symmetrization, multiple lattice space-group identification, primitive-cell behaviors (including standardize=True), and explicit pbc=True usage.
Removed legacy tests
tests/test_symmetry.py
Deleted legacy unittest-based module that previously contained symmetry tests (optional-dependency guards and various symmetry validation cases).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Fix symmetry tests #222 — touches the same legacy tests/test_symmetry.py test code and may overlap with its removal or test adjustments.

Suggested reviewers

  • samwaseda

Poem

"I’m a rabbit in a lab of light,
I hop through lattices late at night,
I nudge cells into periodic sight,
I warn of arrays that took to flight,
🐰🔬 Symmetry snug — everything aligned."

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately describes the primary change: recreating the structure fresh during get_primitive_cell execution, which is the core modification addressing the standardization issue.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch standardize

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

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Outside diff range, codebase verification and nitpick comments (1)
structuretoolkit/analyse/symmetry.py (1)

388-390: Add a period at the end of the warning message.

The warning message should end with a period for consistency.

-            Custom arrays defined in the base structures
-            :attr:`ase.atoms.Atoms.arrays` are not copied to the new structure!
+            Custom arrays defined in the base structures
+            :attr:`ase.atoms.Atoms.arrays` are not copied to the new structure!.
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 8fc7273 and 8f5fbab.

Files selected for processing (2)
  • structuretoolkit/analyse/symmetry.py (1 hunks)
  • tests/test_symmetry.py (2 hunks)
Additional context used
Ruff
structuretoolkit/analyse/symmetry.py

421-421: Undefined name warning

(F821)

Additional comments not posted (3)
tests/test_symmetry.py (2)

176-178: LGTM! The addition of pbc=True is correct.

The change ensures that the structure is treated as periodic, which is necessary for the get_primitive_cell function to work correctly.


204-204: LGTM! The addition of pbc=True is correct.

The change ensures that the structure is treated as periodic, which is necessary for the get_primitive_cell function to work correctly.

structuretoolkit/analyse/symmetry.py (1)

392-393: Good check for periodic structures.

The check ensures that the function only operates on periodic structures, which is necessary for symmetrization.

@jan-janssen jan-janssen marked this pull request as draft July 18, 2024 07:01
This avoids a problem, when you pass standardize=True and the resulting cell would be larger than the original.

Remove array copies and add warning

The trouble is that when spglib returns the symmetrized cell, it may permute, add or remove atoms
so that we cannot tell anymore which values from the original arrays we would need to copy or remove.
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.94%. Comparing base (98e90bd) to head (092cd63).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #144      +/-   ##
==========================================
+ Coverage   82.92%   82.94%   +0.01%     
==========================================
  Files          26       26              
  Lines        1857     1859       +2     
==========================================
+ Hits         1540     1542       +2     
  Misses        317      317              

☔ 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
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the symmetry analysis workflow to rebuild a fresh Atoms object in Symmetry.get_primitive_cell(), avoiding failures when standardize=True produces a cell with more atoms than the input. It also consolidates symmetry tests and expands coverage for periodic boundary conditions and standardized FCC cells.

Changes:

  • Reworked Symmetry.get_primitive_cell() to construct a new structure from spglib’s standardized cell output (and added a strict PBC requirement).
  • Consolidated/expanded symmetry tests into tests/test_analyse_symmetry.py, including new coverage for standardize=True on primitive FCC.
  • Removed the now-redundant tests/test_symmetry.py.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/structuretoolkit/analyse/symmetry.py Rebuilds a fresh structure in get_primitive_cell, adds PBC guard and warnings, and updates error handling.
tests/test_analyse_symmetry.py Adds/relocates symmetry tests and updates structures to use pbc=True, including a new FCC standardization regression test.
tests/test_symmetry.py Removed after moving coverage into test_analyse_symmetry.py.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +405 to +435
.. warning::
Custom arrays defined in the base structures
:attr:`ase.atoms.Atoms.arrays` are not copied to the new structure!
"""
if not all(self._structure.pbc):
raise ValueError("Can only symmetrize periodic structures.")
ret = spglib.standardize_cell(
self._get_spglib_cell(use_elements=use_elements, use_magmoms=use_magmoms),
to_primitive=not standardize,
)
if ret is None:
raise SymmetryError(spglib.error.get_error_message())
cell, positions, indices = ret
positions = (cell.T @ positions.T).T
new_structure = self._structure.copy()
new_structure.cell = cell
new_structure = new_structure[: len(indices)]
raise SymmetryError(spglib.spglib.spglib_error.message)
cell, scaled_positions, indices = ret
indices_dict = {
v: k
for k, v in structuretoolkit.common.helper.get_species_indices_dict(
structure=self._structure
).items()
}
new_structure.symbols = [indices_dict[i] for i in indices]
new_structure.positions = positions
symbols = [indices_dict[i] for i in indices]
arrays = {
k: self._structure.arrays[k]
for k in self._structure.arrays
if k not in ("numbers", "positions")
}
new_structure = type(self._structure)(
symbols=symbols,
scaled_positions=scaled_positions,
cell=cell,
pbc=[True, True, True],
)
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

The docstring warns only about losing custom Atoms.arrays, but the new construction type(self._structure)(...) also drops other ase.Atoms state that copy() would preserve (e.g. info, constraints, calculator, and standard per-atom arrays like initial_magmoms/tags if present). Either preserve the relevant metadata when possible or expand the warning/documentation to reflect all state that will be lost.

Copilot uses AI. Check for mistakes.
pmrv and others added 2 commits April 7, 2026 16:49
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@pmrv pmrv marked this pull request as ready for review April 7, 2026 17:47
@pmrv
Copy link
Copy Markdown
Contributor Author

pmrv commented Apr 7, 2026

@samwaseda I've updated this and merge two test files that we had into one.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/structuretoolkit/analyse/symmetry.py (1)

7-7: logging.warning vs warnings.warn - different semantics.

The import from logging import warning is valid, but logging.warning() and warnings.warn() serve different purposes:

  • logging.warning() logs to the logging system (may be silently ignored if logging isn't configured)
  • warnings.warn() issues a Python warning that users can filter/catch and is more appropriate for deprecation-style messages

For alerting users about data loss (custom arrays not carried over), warnings.warn() is typically more appropriate as it's more visible by default.

♻️ Suggested change to use warnings.warn
-from logging import warning
+import warnings

And in line 433:

-            warning(f"Custom arrays {keys} do not carry over to new structure!")
+            warnings.warn(f"Custom arrays {keys} do not carry over to new structure!")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/structuretoolkit/analyse/symmetry.py` at line 7, The code currently
imports logging.warning and uses it to notify about custom arrays not being
carried over; change the import to use the warnings module and call
warnings.warn(...) instead of warning(...). Specifically, remove or replace
"from logging import warning" with "import warnings" and update the call site
that emits the custom-arrays/data-loss message (the warning currently emitted
around the data-loss handling in symmetry.py) to use
warnings.warn("...message...", category=UserWarning, stacklevel=2) so users can
filter/catch the warning; keep the original message text and adjust any
tests/handlers expecting logging.warning accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/structuretoolkit/analyse/symmetry.py`:
- Line 7: The code currently imports logging.warning and uses it to notify about
custom arrays not being carried over; change the import to use the warnings
module and call warnings.warn(...) instead of warning(...). Specifically, remove
or replace "from logging import warning" with "import warnings" and update the
call site that emits the custom-arrays/data-loss message (the warning currently
emitted around the data-loss handling in symmetry.py) to use
warnings.warn("...message...", category=UserWarning, stacklevel=2) so users can
filter/catch the warning; keep the original message text and adjust any
tests/handlers expecting logging.warning accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89cb8da8-d655-4a39-ad55-bf5063a6c247

📥 Commits

Reviewing files that changed from the base of the PR and between 8f5fbab and 15726ec.

📒 Files selected for processing (3)
  • src/structuretoolkit/analyse/symmetry.py
  • tests/test_analyse_symmetry.py
  • tests/test_symmetry.py
💤 Files with no reviewable changes (1)
  • tests/test_symmetry.py

jan-janssen and others added 3 commits April 10, 2026 22:11
Co-authored-by: Jan Janssen <jan-janssen@users.noreply.github.com>
* Extend unit tests for get_primitive_cell()

Added test cases to cover:
- Periodic boundary condition validation: ensuring ValueError is raised if structure is not periodic.
- Custom arrays warning: ensuring a warning is logged when custom arrays are present in the source structure.

Co-authored-by: jan-janssen <3854739+jan-janssen@users.noreply.github.com>

* fixes

* another fix

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: jan-janssen <3854739+jan-janssen@users.noreply.github.com>
Copy link
Copy Markdown
Member

@jan-janssen jan-janssen left a comment

Choose a reason for hiding this comment

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

Looks good to me

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
tests/test_analyse_symmetry.py (1)

332-339: ⚠️ Potential issue | 🟠 Major

Fix the functional get_equivalent_points test to assert equivalence-class IDs, not coordinates.

test_get_equivalent_points() calls .generate_equivalent_points() and correctly asserts the minimum distance is 0 (the point lands on [0.51, 0.5, 0]). However, test_get_equivalent_points_functional() calls the functional get_equivalent_points(), which delegates to .get_arg_equivalent_sites() and returns equivalence-class IDs, not coordinates. The test incorrectly treats the IDs as coordinates, computing norm(arr - [0.51, 0.5, 0], axis=-1). When arr = [0] (the equivalence class ID), this norm equals 0.7142128534267638—exactly the norm of the coordinate vector itself—making the test pass despite checking the wrong output type.

Either use .generate_equivalent_points() to match the instance-method test, or assert the correct equivalence-class IDs.

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

In `@tests/test_analyse_symmetry.py` around lines 332 - 339, The functional test
calls get_equivalent_points (which delegates to get_arg_equivalent_sites) and
thus returns equivalence-class IDs, not coordinates; update
test_get_equivalent_points_functional to assert the returned IDs (e.g., that arr
equals the expected class id like [0] or contains 0) instead of computing
distances to coordinate [0.51, 0.5, 0], or alternatively call
generate_equivalent_points (matching the instance-method test) if you intended
to compare coordinates; adjust the assertion to use get_equivalent_points (IDs)
or generate_equivalent_points (coordinates) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_analyse_symmetry.py`:
- Around line 451-457: The test test_error currently allows either
stk.common.SymmetryError or SpglibError which masks regressions in exception
wrapping; update the assertion in tests/test_analyse_symmetry.py so the with
self.assertRaises only expects stk.common.SymmetryError (remove SpglibError),
ensuring stk.analyse.get_symmetry raises the wrapped SymmetryError as documented
and the docstring remains accurate.

---

Outside diff comments:
In `@tests/test_analyse_symmetry.py`:
- Around line 332-339: The functional test calls get_equivalent_points (which
delegates to get_arg_equivalent_sites) and thus returns equivalence-class IDs,
not coordinates; update test_get_equivalent_points_functional to assert the
returned IDs (e.g., that arr equals the expected class id like [0] or contains
0) instead of computing distances to coordinate [0.51, 0.5, 0], or alternatively
call generate_equivalent_points (matching the instance-method test) if you
intended to compare coordinates; adjust the assertion to use
get_equivalent_points (IDs) or generate_equivalent_points (coordinates)
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f94995a-72c3-4844-8d0c-2e1ffae75922

📥 Commits

Reviewing files that changed from the base of the PR and between 012dadd and 092cd63.

📒 Files selected for processing (1)
  • tests/test_analyse_symmetry.py

Comment on lines +451 to +457
def test_error(self):
"""spglib errors should be wrapped in a SymmetryError."""

structure = bulk("Al")
structure += structure[-1]
with self.assertRaises((stk.common.SymmetryError, SpglibError)):
stk.analyse.get_symmetry(structure=structure)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Keep this test aligned with the documented error contract.

The docstring says spglib failures are wrapped in stk.common.SymmetryError, but the assertion now also accepts raw SpglibError. That makes this test pass even if the wrapping regresses.

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

In `@tests/test_analyse_symmetry.py` around lines 451 - 457, The test test_error
currently allows either stk.common.SymmetryError or SpglibError which masks
regressions in exception wrapping; update the assertion in
tests/test_analyse_symmetry.py so the with self.assertRaises only expects
stk.common.SymmetryError (remove SpglibError), ensuring stk.analyse.get_symmetry
raises the wrapped SymmetryError as documented and the docstring remains
accurate.

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

Labels

bug Something isn't working enhancement New feature or request format_black

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants