Skip to content

Conversation

utf
Copy link

@utf utf commented Jul 31, 2025

Description

This PR is WIP while we finalise the list of supported mixed valence elements.

Added support for mixed valence compounds in the smact_validity function. The default behaviour of the function has not changed. For example:

from smact.screening import smact_validity

smact_validity("Fe3O4")
# returns False

smact_validity("Fe3O4", mixed_valence=True)
# returns True

The algorithm is as follows:

  1. If a composition passes the regular oxidation state checking, the function proceeds as normal.
  2. If a composition cannot be made to charge balance we first check if it contains any elements that are known to result in mixed valence compositions. The choice of these elements was based on screening of the ICSD.
  3. If so, we "expand" the composition to treat potential mixed valence element as separate species. For example, Fe3O4 would be treated as FeFeFeO4.
  4. We then perform the oxidation state checking again using the expanded composition.

The algorithm will fail if the reduced formula does not support mixed valence elements due to limited multiplicity. For example, Cs2SbCl6, which contains Sb3+ and Sb5+. To overcome this, a future fix could be to add another option to repeat the composition up to a fixed number (i.e., Cs4SbSbCl12).

This PR addresses #196 and #418.

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

How Has This Been Tested?

Added a test_smact_validity_mixed_valence in tests_core.py.

Reviewers

@Panyalak @dandavies99

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • I have checked my code and corrected any misspellings

Summary by CodeRabbit

  • New Features
    • Added support for handling mixed valence elements in compound validity checks via a new option.
  • Tests
    • Introduced tests to verify correct behaviour for compounds with mixed valence elements.

Copy link
Contributor

coderabbitai bot commented Jul 31, 2025

Walkthrough

A new mechanism for handling mixed valence elements was introduced in the smact_validity function via a new parameter and helper functions. The function now optionally expands compositions containing mixed valence elements and re-validates them. Associated tests were added to verify this behaviour, specifically for compounds like Fe₃O₄.

Changes

Cohort / File(s) Change Summary
Mixed valence handling in screening
smact/screening.py
Introduced the MIXED_VALENCE_ELEMENTS list and a mixed_valence parameter to smact_validity. Added helper functions _expand_mixed_valence_comp and _is_valid_oxi_state to manage and validate expanded compositions. Refactored the control flow to support optional mixed valence checking and improved code modularity. Removed unused imports and reorganised logic for clarity.
Testing mixed valence support
smact/tests/test_core.py
Added a test method test_smact_validity_mixed_valence to check the new mixed valence functionality. The test asserts correct behaviour for Fe₃O₄ with and without the mixed_valence flag.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant smact_validity
    participant _is_valid_oxi_state
    participant _expand_mixed_valence_comp

    User->>smact_validity: Call with composition, mixed_valence flag
    smact_validity->>_is_valid_oxi_state: Validate original composition
    _is_valid_oxi_state-->>smact_validity: Return validity result
    alt No valid combination and mixed_valence enabled
        smact_validity->>_expand_mixed_valence_comp: Expand composition
        _expand_mixed_valence_comp-->>smact_validity: Return expanded composition
        smact_validity->>_is_valid_oxi_state: Validate expanded composition
        _is_valid_oxi_state-->>smact_validity: Return validity result
    end
    smact_validity-->>User: Return True/False
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Possibly related PRs

Suggested labels

enhancement, python, feature, refactor

Suggested reviewers

  • ryannduma
  • AntObi

Poem

In the warren of code, a new path we find,
For iron and friends with valence entwined.
Now Fe₃O₄ is truly seen,
With helpers and tests, our logic is keen.
Mixed valence, no longer a riddle—
🐇 hops forward, right to the middle!

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mixed-valence

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 generate unit tests to generate unit tests for 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 or @coderabbitai title 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

codecov bot commented Jul 31, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.83%. Comparing base (515a369) to head (0096933).

Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #441      +/-   ##
===========================================
+ Coverage    80.66%   80.83%   +0.16%     
===========================================
  Files           33       33              
  Lines         2871     2896      +25     
===========================================
+ Hits          2316     2341      +25     
  Misses         555      555              

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

🔭 Outside diff range comments (1)
smact/screening.py (1)

584-601: Add comprehensive docstring for clarity.

The function encapsulates important logic but lacks documentation.

 def _is_valid_oxi_state(ox_combos, stoichs, threshold, electronegs, use_pauling_test=True):
-    """Utility function to check if there is a valid oxidation state solution."""
+    """
+    Check if there is a valid oxidation state combination that satisfies charge neutrality and electronegativity rules.
+    
+    Args:
+        ox_combos: List of possible oxidation states for each element
+        stoichs: List of stoichiometries for each element
+        threshold: Maximum stoichiometry value for neutral ratio calculations
+        electronegs: List of electronegativities for each element
+        use_pauling_test: Whether to apply Pauling electronegativity test (default: True)
+        
+    Returns:
+        bool: True if a valid oxidation state combination exists, False otherwise
+    """
     for ox_states in itertools.product(*ox_combos):
🧹 Nitpick comments (2)
smact/screening.py (1)

24-45: Consider using a set for better lookup performance.

Since MIXED_VALENCE_ELEMENTS is only used for membership testing (line 559), converting it to a set would improve lookup performance from O(n) to O(1).

-MIXED_VALENCE_ELEMENTS = [
+MIXED_VALENCE_ELEMENTS = {
     "Fe",
     "Mn",
     "Co",
     "Cu",
     "Ni",
     "V",
     "Ti",
     "Cr",
     "Nb",
     "Mo",
     "W",
     "Re",
     "Ru",
     "Os",
     "Pd",
     "Ag",
     "Au",
     "Sn",
     "Sb",
     "Bi",
-]
+}
smact/tests/test_core.py (1)

487-496: Consider adding more comprehensive test cases.

While the Fe₃O₄ test is excellent, consider adding tests for:

  1. Other mixed valence compounds (e.g., with Mn, V, or Cu)
  2. The edge case mentioned in PR objectives (Cs₂SbCl₆)
  3. Regular compounds with mixed_valence=True to ensure no false positives

Would you like me to generate additional test cases to improve coverage of the mixed valence functionality?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 636fbb8 and 0096933.

📒 Files selected for processing (2)
  • smact/screening.py (4 hunks)
  • smact/tests/test_core.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
smact/tests/test_core.py (1)
smact/screening.py (1)
  • smact_validity (448-564)
smact/screening.py (1)
smact/__init__.py (1)
  • _gcd_recursive (450-455)
🪛 Ruff (0.12.2)
smact/screening.py

482-482: import should be at the top-level of a file

(PLC0415)

⏰ 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). (2)
  • GitHub Check: test (3.13, macos-latest)
  • GitHub Check: test (3.11, windows-latest)
🔇 Additional comments (2)
smact/screening.py (2)

458-458: Good backward-compatible implementation.

The addition of the mixed_valence parameter with a default value of False maintains backward compatibility while adding the new functionality.


555-564: Well-structured implementation of mixed valence logic.

The refactoring cleanly separates the oxidation state validation logic and implements the mixed valence expansion algorithm as described in the PR objectives. The short-circuiting behaviour is preserved.

Comment on lines +567 to +582
def _expand_mixed_valence_comp(ox_combos, stoichs, electronegs, elem_symbols):
"""Utility function to expand mixed valence elements in the composition."""
new_ox_combos = []
new_stoichs = []
new_electronegs = []
for el, ox, count, electrnoeg in zip(elem_symbols, ox_combos, stoichs, electronegs, strict=False):
if el in MIXED_VALENCE_ELEMENTS:
new_ox_combos.extend([ox] * count[0])
new_electronegs.extend([electrnoeg] * count[0])
new_stoichs.extend([(1,)] * count[0])
else:
new_ox_combos.append(ox)
new_electronegs.append(electrnoeg)
new_stoichs.append(count)
return new_ox_combos, new_stoichs, new_electronegs

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add docstring and fix typo in parameter name.

The function logic is correct, but needs documentation and has a typo in the parameter name.

 def _expand_mixed_valence_comp(ox_combos, stoichs, electronegs, elem_symbols):
-    """Utility function to expand mixed valence elements in the composition."""
+    """
+    Expand mixed valence elements in a composition by treating each as a separate species.
+    
+    Args:
+        ox_combos: List of possible oxidation states for each element
+        stoichs: List of stoichiometries (as tuples) for each element
+        electronegs: List of electronegativities for each element
+        elem_symbols: List of element symbols
+        
+    Returns:
+        tuple: Expanded (ox_combos, stoichs, electronegs) where mixed valence 
+               elements are replicated according to their stoichiometry
+    """
     new_ox_combos = []
     new_stoichs = []
     new_electronegs = []
-    for el, ox, count, electrnoeg in zip(elem_symbols, ox_combos, stoichs, electronegs, strict=False):
+    for el, ox, count, electronegs in zip(elem_symbols, ox_combos, stoichs, electronegs, strict=False):
         if el in MIXED_VALENCE_ELEMENTS:
             new_ox_combos.extend([ox] * count[0])
-            new_electronegs.extend([electrnoeg] * count[0])
+            new_electronegs.extend([electronegs] * count[0])
             new_stoichs.extend([(1,)] * count[0])
         else:
             new_ox_combos.append(ox)
-            new_electronegs.append(electrnoeg)
+            new_electronegs.append(electronegs)
             new_stoichs.append(count)
     return new_ox_combos, new_stoichs, new_electronegs

Also consider using strict=True in the zip() call to catch any length mismatches between the input lists, as this would indicate a programming error.

📝 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 _expand_mixed_valence_comp(ox_combos, stoichs, electronegs, elem_symbols):
"""Utility function to expand mixed valence elements in the composition."""
new_ox_combos = []
new_stoichs = []
new_electronegs = []
for el, ox, count, electrnoeg in zip(elem_symbols, ox_combos, stoichs, electronegs, strict=False):
if el in MIXED_VALENCE_ELEMENTS:
new_ox_combos.extend([ox] * count[0])
new_electronegs.extend([electrnoeg] * count[0])
new_stoichs.extend([(1,)] * count[0])
else:
new_ox_combos.append(ox)
new_electronegs.append(electrnoeg)
new_stoichs.append(count)
return new_ox_combos, new_stoichs, new_electronegs
def _expand_mixed_valence_comp(ox_combos, stoichs, electronegs, elem_symbols):
"""
Expand mixed valence elements in a composition by treating each as a separate species.
Args:
ox_combos: List of possible oxidation states for each element
stoichs: List of stoichiometries (as tuples) for each element
electronegs: List of electronegativities for each element
elem_symbols: List of element symbols
Returns:
tuple: Expanded (ox_combos, stoichs, electronegs) where mixed valence
elements are replicated according to their stoichiometry
"""
new_ox_combos = []
new_stoichs = []
new_electronegs = []
for el, ox, count, electronegs in zip(elem_symbols, ox_combos, stoichs, electronegs, strict=False):
if el in MIXED_VALENCE_ELEMENTS:
new_ox_combos.extend([ox] * count[0])
new_electronegs.extend([electronegs] * count[0])
new_stoichs.extend([(1,)] * count[0])
else:
new_ox_combos.append(ox)
new_electronegs.append(electronegs)
new_stoichs.append(count)
return new_ox_combos, new_stoichs, new_electronegs
🤖 Prompt for AI Agents
In smact/screening.py around lines 567 to 582, add a clear docstring explaining
the purpose and parameters of the _expand_mixed_valence_comp function. Fix the
typo in the parameter name from 'electrnoeg' to 'electronegs' in the zip() call
and throughout the function. Also, change the zip() call to use strict=True to
ensure input lists have matching lengths and catch potential programming errors
early.

@AntObi AntObi self-assigned this Jul 31, 2025
@AntObi AntObi changed the base branch from master to develop July 31, 2025 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants