Skip to content

Conversation

@jan-janssen
Copy link
Member

@jan-janssen jan-janssen commented Jan 9, 2026

This pull request includes the changes in:

Example:

import os
import pandas
from atomistics.calculators.lammps.melting import estimate_melting_temperature

potential = pandas.DataFrame(
    {
        'Config': [['pair_style eam/fs \n', 'pair_coeff * * Al1.eam.fs Al\n']],
        'Filename': [[os.path.abspath('./Al1.eam.fs')]],
        'Model': ['Lammps'],
        'Name': ['CustomPotential'],
        'Species': [['Al']],
    }
)

estimate_melting_temperature(
    element="Al", 
    potential=potential, 
    strain_run_time_steps=1000, 
    temperature_left=0, 
    temperature_right=1000, 
    number_of_atoms=8000, 
    seed=None,
)

Summary by CodeRabbit

  • New Features

    • Melting-temperature estimation workflow using LAMMPS simulations.
    • Configurable initial velocity rescaling for MD.
    • vmax option for structure optimization.
    • XYZ-coupled ensemble option for NPT MD.
  • Improvements

    • Propagated velocity rescaling and vmax across MD routines and input templates for greater flexibility.
  • Tests

    • Added melting workflow test and updated MD tests for velocity-rescale behavior.
  • Chores

    • CI environment updated with an additional dependency.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

📝 Walkthrough

Walkthrough

Parameterizes initial LAMMPS velocity scaling via a new velocity_rescale_factor, replaces disable_initial_velocity with this parameter in MD functions, adds vmax and couple_xyz options, and introduces a new melting-temperature estimation module using iterative minimization, MD, and structure analysis.

Changes

Cohort / File(s) Summary
LAMMPS Command Template
src/atomistics/calculators/lammps/commands.py
LAMMPS_VELOCITY updated: hardcoded 2 replaced with template variable {{ velocity_rescale_factor }} in the velocity creation command.
MD Helpers
src/atomistics/calculators/lammps/helpers.py
Added optional velocity_rescale_factor: float = 2.0 parameter to lammps_thermal_expansion_loop() and passed it into the template render context.
Calculator Library
src/atomistics/calculators/lammps/libcalculator.py
Added Optional typing, introduced vmax: Optional[float] and internal _get_vmax_command(), replaced disable_initial_velocity usage with velocity_rescale_factor: Optional[float], added couple_xyz: bool option, and propagated velocity_rescale_factor into MD input templates and run command selection.
Melting Module (new)
src/atomistics/calculators/lammps/melting.py
New module implementing bisection-based estimate_melting_temperature_using_bisection_CNA() with helpers for replication, minimization, NPT MD runs, structure analysis, and iterative temperature refinement.
Tests
tests/test_lammpslib_md.py, tests/test_melting_lammps.py
Replaced disable_initial_velocity=True with velocity_rescale_factor=None in tests; added tests/test_melting_lammps.py with a conditional LAMMPS-dependent melting-temperature estimation test.
CI Environment
.ci_support/environment-lammps.yml
Added pyscal3 = 3.3.0 dependency.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Estimate as "estimate_melting_temperature"
    participant Minimizer as "Minimizer"
    participant Analyzer as "StructureAnalyzer"
    participant MD as "MD_NPT"
    participant Bisection as "_next_step_funct"

    User->>Estimate: request(structure, potential, T_left, T_right, target_atoms)
    Estimate->>Minimizer: build & minimize initial structure
    Minimizer-->>Estimate: minimized_structure
    Estimate->>Analyzer: analyze minimized_structure
    Analyzer-->>Estimate: initial_distribution

    loop until (T_right - T_left) ≤ tolerance
        Estimate->>Bisection: current bounds, structures, distribution
        Bisection->>MD: run NPT MD at selected temperature
        MD-->>Bisection: md_result
        Bisection->>Minimizer: minimize md_result
        Minimizer-->>Bisection: minimized_md
        Bisection->>Analyzer: analyze minimized_md
        Analyzer-->>Bisection: new_distribution
        Bisection-->>Estimate: updated bounds & structures
    end

    Estimate-->>User: final estimated melting temperature
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • samwaseda

Poem

🐇 I nudged a "2" to be configurable, neat and spry,
Velocities now scale where once a constant did lie.
I hopped through melts — minimize, heat, analyze with care,
Bisecting temps and structures till answers were fair.
Cheers — a rabbit applauds this scientific try!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a workflow to estimate melting temperature using LAMMPS, which aligns with the new melting.py module and supporting parameter changes.

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

✨ 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

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

@jan-janssen jan-janssen requested a review from prabhath-c January 9, 2026 13:34
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: 0

🧹 Nitpick comments (5)
src/atomistics/calculators/lammps/melting.py (4)

67-75: Incomplete docstring with stale content.

The docstring references ham (GenericJob) which doesn't match the actual parameter structure. The Args and Returns sections are empty.

📝 Suggested docstring update
 def _analyse_minimized_structure(structure):
     """
+    Analyze a minimized structure to determine its dominant crystal type.
 
     Args:
-        ham (GenericJob):
+        structure (ase.Atoms): Atomistic structure object to analyze.
 
     Returns:
-
+        tuple: (structure, key_max, number_of_atoms, distribution_initial_half, final_structure_dict)
+            - structure: The input structure
+            - key_max: Key of the dominant structure type
+            - number_of_atoms: Total number of atoms
+            - distribution_initial_half: Half of the fraction of atoms in the dominant type
+            - final_structure_dict: Full descriptor dictionary
     """

93-108: Docstring contains irrelevant parameter descriptions.

The docstring mentions parameters like job_type, project, potential, and queue that don't exist in this function's signature. This appears to be carried over from a different codebase (pyiron_atomistics).

📝 Suggested docstring cleanup
 def _next_calc(structure, potential, temperature, seed, run_time_steps=10000):
     """
-    Calculate NPT ensemble at a given temperature using the job defined in the project parameters:
-    - job_type: Type of Simulation code to be used
-    - project: Project object used to create the job
-    - potential: Interatomic Potential
-    - queue (optional): HPC Job queue to be used
+    Run NPT molecular dynamics at a given temperature using LAMMPS.
 
     Args:
-        structure (pyiron_atomistics.structure.atoms.Atoms): Atomistic Structure object to be set to the job as input sturcture
+        structure (ase.Atoms): Atomistic structure object for the simulation.
+        potential: Interatomic potential dataframe.
         temperature (float): Temperature of the Molecular dynamics calculation
+        seed (int): Random seed for velocity initialization.
         run_time_steps (int): Number of Molecular dynamics steps
 
     Returns:
-        Final Atomistic Structure object
+        ase.Atoms: Final structure after MD equilibration.
     """

133-219: Bisection logic for melting point estimation looks correct.

The control flow handles the three expected scenarios: both crystalline (shift right), left crystalline/right melted (bisect), both melted (shift left). The ValueError on line 218 is appropriate as a defensive assertion for an unreachable state.

The docstring is incomplete (empty Args/Returns), similar to _analyse_minimized_structure.


294-294: Redundant int() call.

In Python 3, round() with a single argument already returns an integer, making the outer int() call unnecessary.

♻️ Suggested fix
-    return int(round(temperature_left))
+    return round(temperature_left)
src/atomistics/calculators/lammps/libcalculator.py (1)

613-620: Type check is overly restrictive.

The isinstance(vmax, float) check will reject integer values like 1 or expressions that evaluate to integers. Since LAMMPS accepts numeric values for vmax, consider accepting both int and float.

♻️ Suggested fix
 def _get_vmax_command(vmax: Optional[float]) -> str:
     if vmax is not None:
-        if isinstance(vmax, float):
+        if isinstance(vmax, (int, float)):
             return LAMMPS_MINIMIZE_VOLUME + " vmax {vmax}".format(vmax=vmax)
         else:
-            raise TypeError("vmax must be a float.")
+            raise TypeError("vmax must be a numeric value.")
     else:
         return LAMMPS_MINIMIZE_VOLUME
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aee9634 and 7228f4b.

📒 Files selected for processing (5)
  • src/atomistics/calculators/lammps/commands.py
  • src/atomistics/calculators/lammps/helpers.py
  • src/atomistics/calculators/lammps/libcalculator.py
  • src/atomistics/calculators/lammps/melting.py
  • tests/test_lammpslib_md.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/atomistics/calculators/lammps/libcalculator.py (1)
src/atomistics/shared/output.py (2)
  • OutputMolecularDynamics (42-51)
  • keys (11-18)
🪛 Ruff (0.14.10)
src/atomistics/calculators/lammps/melting.py

218-218: Avoid specifying long messages outside the exception class

(TRY003)


232-232: Standard pseudo-random generators are not suitable for cryptographic purposes

(S311)


294-294: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)

src/atomistics/calculators/lammps/libcalculator.py

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

(B008)


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

(B008)


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

(B008)


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

(B008)


618-618: Avoid specifying long messages outside the exception class

(TRY003)

⏰ 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). (6)
  • GitHub Check: unittest_sphinxdft
  • GitHub Check: unittest_qe
  • GitHub Check: unittest_matrix (windows-latest, 3.14)
  • GitHub Check: unittest_gpaw
  • GitHub Check: notebooks
  • GitHub Check: coverage
🔇 Additional comments (12)
src/atomistics/calculators/lammps/helpers.py (1)

105-119: LGTM!

The velocity_rescale_factor parameter is cleanly integrated into the thermal expansion loop. It's properly added to the function signature with a sensible default value of 2.0 and correctly propagated to the template rendering context.

src/atomistics/calculators/lammps/melting.py (2)

17-36: LGTM!

The diamond detection heuristic is reasonable—comparing the "OTHER" counts from CNA vs diamond descriptors to determine which analysis method better classifies the structure.


39-64: LGTM!

Clean dispatcher function with appropriate documentation.

tests/test_lammpslib_md.py (2)

74-74: LGTM!

The test correctly uses velocity_rescale_factor=None to disable initial velocity assignment, aligning with the updated API. The assertion that temperature stays below 1 correctly verifies the expected behavior.


166-166: LGTM!

Consistent with the NVT test update—correctly uses velocity_rescale_factor=None for the NPT no-velocity test case.

src/atomistics/calculators/lammps/commands.py (1)

23-23: LGTM!

The velocity command template correctly replaces the hardcoded factor 2 with the parameterized {{ velocity_rescale_factor }}. The LAMMPS velocity all create command syntax with the arithmetic expression $(...) is valid.

src/atomistics/calculators/lammps/libcalculator.py (6)

52-57: LGTM!

The vmax parameter is cleanly integrated with proper Optional typing and delegates command generation to the _get_vmax_command helper.


158-184: LGTM!

The velocity_rescale_factor parameter is properly integrated. When None, the velocity command is correctly omitted from the template. The branching logic is clean and the parameter is properly passed to the template render.


237-257: LGTM!

The couple_xyz option is cleanly implemented. Consider using lowercase for the local variable (lammps_ensemble_npt_xyz) to distinguish it from module-level constants, but this is a minor style preference.


325-351: LGTM!

Consistent implementation of velocity_rescale_factor parameter following the same pattern as NVT and NPT functions.


401-429: LGTM!

Consistent velocity_rescale_factor implementation for Langevin dynamics.


486-505: LGTM!

The couple_xyz parameter is correctly integrated. The function uses the default velocity_rescale_factor=2.0 from lammps_thermal_expansion_loop, which is appropriate for thermal expansion calculations.

@codecov
Copy link

codecov bot commented Jan 9, 2026

Codecov Report

❌ Patch coverage is 90.90909% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.92%. Comparing base (aee9634) to head (98d66f5).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/atomistics/calculators/lammps/libcalculator.py 72.22% 5 Missing ⚠️
src/atomistics/calculators/lammps/melting.py 95.65% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #610      +/-   ##
==========================================
+ Coverage   86.81%   86.92%   +0.10%     
==========================================
  Files          43       44       +1     
  Lines        2419     2500      +81     
==========================================
+ Hits         2100     2173      +73     
- Misses        319      327       +8     

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

🤖 Fix all issues with AI agents
In @tests/test_melting_lammps.py:
- Around line 24-32: The test is non-deterministic because
estimate_melting_temperature is called with seed=None which yields random
results; update the test call in tests/test_melting_lammps.py to pass a fixed
integer seed (e.g., seed=42) to estimate_melting_temperature and then re-run the
test to capture the deterministic expected melting_temp value and update the
assertion to that single expected value; reference the
estimate_melting_temperature call and the seed handling in melting.py (around
where seed is checked/initialized) when making the change.
🧹 Nitpick comments (2)
tests/test_melting_lammps.py (2)

33-33: Document the expected values in the assertion.

The assertion accepts two specific values without explanation. Add a comment clarifying why these values are expected (e.g., known melting temperature range for Al with this potential, tolerance for numerical precision, etc.).

📝 Proposed documentation
-    self.assertIn(melting_temp, [1008, 1023])
+    # Expected melting temperature for Al with Mishin-1999 potential: ~1008-1023 K
+    # (experimental Al melting point: 933 K; potential-specific deviation expected)
+    self.assertIn(melting_temp, [1008, 1023])

24-32: Consider test performance impact.

The test uses large parameters (number_of_atoms=8000, strain_run_time_steps=1000) that may result in slow execution during CI/CD. If runtime becomes an issue, consider:

  • Reducing parameters for faster feedback (e.g., 1000-2000 atoms, 100-500 steps)
  • Moving to integration/nightly test suite
  • Adding a faster smoke test with minimal parameters
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7228f4b and 5f5d5ff.

📒 Files selected for processing (1)
  • tests/test_melting_lammps.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_melting_lammps.py (1)
src/atomistics/calculators/lammps/melting.py (1)
  • estimate_melting_temperature (222-294)
⏰ 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). (15)
  • GitHub Check: unittest_old
  • GitHub Check: notebooks
  • GitHub Check: unittest_qe
  • GitHub Check: unittest_siesta
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.11)
  • GitHub Check: unittest_gpaw
  • GitHub Check: unittest_sphinxdft
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.14)
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.12)
  • GitHub Check: unittest_matrix (macos-latest, 3.14)
  • GitHub Check: unittest_grace
  • GitHub Check: unittest_matrix (windows-latest, 3.14)
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.13)
  • GitHub Check: unittest_mace
  • GitHub Check: coverage

Comment on lines +24 to +32
melting_temp = estimate_melting_temperature(
element="Al",
potential=potential,
strain_run_time_steps=1000,
temperature_left=0,
temperature_right=1000,
number_of_atoms=8000,
seed=None,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Non-deterministic test: fix the seed parameter.

Passing seed=None triggers random seed generation in estimate_melting_temperature (see melting.py lines 228-229), causing non-deterministic test results. This explains why the assertion accepts two values [1008, 1023], but makes the test flaky and unreliable for CI/CD.

🎲 Proposed fix: use a fixed seed
     melting_temp = estimate_melting_temperature(
         element="Al", 
         potential=potential, 
         strain_run_time_steps=1000, 
         temperature_left=0, 
         temperature_right=1000, 
         number_of_atoms=8000, 
-        seed=None,
+        seed=42,
     )
-    self.assertIn(melting_temp, [1008, 1023])
+    self.assertEqual(melting_temp, 1008)  # or 1023, depending on seed=42 result

Run the test with a fixed seed to determine the expected value, then update the assertion accordingly.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @tests/test_melting_lammps.py around lines 24 - 32, The test is
non-deterministic because estimate_melting_temperature is called with seed=None
which yields random results; update the test call in
tests/test_melting_lammps.py to pass a fixed integer seed (e.g., seed=42) to
estimate_melting_temperature and then re-run the test to capture the
deterministic expected melting_temp value and update the assertion to that
single expected value; reference the estimate_melting_temperature call and the
seed handling in melting.py (around where seed is checked/initialized) when
making the change.

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

🧹 Nitpick comments (1)
.ci_support/environment-lammps.yml (1)

11-11: Minor formatting inconsistency with existing dependencies.

The spacing around the = sign is inconsistent with the existing dependencies (lines 4-10), which use =version format without a space before the equals sign.

📝 Suggested formatting adjustment
-- pyscal3 = 3.3.0
+- pyscal3 =3.3.0
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f5d5ff and 98d66f5.

📒 Files selected for processing (1)
  • .ci_support/environment-lammps.yml
⏰ 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). (15)
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.14)
  • GitHub Check: unittest_matrix (windows-latest, 3.14)
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.11)
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.12)
  • GitHub Check: unittest_grace
  • GitHub Check: unittest_old
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.13)
  • GitHub Check: unittest_matrix (macos-latest, 3.14)
  • GitHub Check: unittest_sphinxdft
  • GitHub Check: unittest_qe
  • GitHub Check: unittest_gpaw
  • GitHub Check: unittest_mace
  • GitHub Check: unittest_siesta
  • GitHub Check: notebooks
  • GitHub Check: coverage
🔇 Additional comments (1)
.ci_support/environment-lammps.yml (1)

11-11: pyscal3 version 3.3.0 is the latest release on conda-forge. No updates are needed.

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

🤖 Fix all issues with AI agents
In `@src/atomistics/calculators/lammps/melting.py`:
- Around line 78-85: The repetition candidates can include zero when r_est < 1
because np.round(r_est) may return 0, causing input_structure.repeat([0,0,0]) to
produce an empty structure; update the construction of candidates/basis_lst so
every repeat factor is at least 1 (e.g., apply max(1, ...) to the round and ceil
results or clip the entire candidates array to >=1) before building basis_lst
and selecting basis, referencing r_est, candidates, basis_lst, basis and
input_structure.repeat.
- Around line 1-6: The import currently brings in the random() function (from
random import random) but the code later calls random.randint(0, 99999) (e.g.,
in functions/methods referencing random.randint), causing AttributeError; change
the import so the module provides randint (either use import random or from
random import randint) and update any references if you choose the latter so
calls to random.randint become just randint; locate the import at the top of the
file and replace it accordingly to fix uses like random.randint.
♻️ Duplicate comments (1)
tests/test_melting_lammps.py (1)

23-35: Non-deterministic test due to seed=None.

Passing seed=None triggers random seed generation in estimate_melting_temperature_using_bisection_CNA (line 406-407 in melting.py), causing non-deterministic test results. The assertion accepting two values [1008, 1023] confirms flakiness.

Use a fixed seed for reproducible CI/CD results:

🎲 Proposed fix
         melting_temp = estimate_melting_temperature_using_bisection_CNA(
             structure=input_structure,
             potential_dataframe=potential_dataframe,
             target_number_of_atoms=4000,
             temperature_left=0,
             temperature_right=1000,
             temperature_diff_tolerance=10,
             run=1000,
-            seed=None,
+            seed=42,
             cores=1,
             log_file=None,
         )
-        self.assertIn(melting_temp, [1008, 1023])
+        self.assertEqual(melting_temp, <expected_value>)  # Run once with seed=42 to determine
🧹 Nitpick comments (6)
src/atomistics/calculators/lammps/melting.py (6)

19-61: LGTM with optional suggestion.

The NPT MD wrapper correctly configures velocity rescaling and xyz coupling. The structure copy-and-update pattern is sound.

Consider replacing print() statements with Python's logging module for better control over verbosity in library code, but this is optional for an initial implementation.


138-166: LGTM!

The analysis extraction logic is correct. Consider using a namedtuple or dataclass for the return type to improve readability, but this is optional.


285-286: Potential unreachable case or missing branch.

The else branch catches when structure_right_dict[key_max] / number_of_atoms > distribution_initial_half > structure_left_dict[key_max] / number_of_atoms (the inverse of line 242-246). This could occur if the initial temperature bounds are inverted or if the structure analysis yields unexpected results.

Consider either:

  1. Adding an explicit branch to handle this case, or
  2. Improving the error message to aid debugging

Per static analysis (TRY003), consider moving the message to a custom exception class.

📝 Suggested improvement
     else:
-        raise ValueError("We should never reach this point!")
+        raise ValueError(
+            f"Unexpected bisection state: left={structure_left_dict[key_max]/number_of_atoms:.3f}, "
+            f"right={structure_right_dict[key_max]/number_of_atoms:.3f}, threshold={distribution_initial_half:.3f}"
+        )

332-344: FIXME: temperature_left assumed to be solid state.

The FIXME comment correctly identifies that the algorithm assumes the left bound structure is the optimized (solid) structure without running MD. If a user passes temperature_left > 0, the assumption may be invalid.

Consider either:

  1. Running MD at temperature_left if it's above a threshold (e.g., > 0), or
  2. Documenting this limitation in the docstring

Would you like me to propose a fix that validates the left bound when temperature_left > 0?


440-440: Unnecessary int() cast.

round() with a single argument already returns an int in Python 3, making the outer int() call redundant (per static analysis RUF046).

♻️ Proposed fix
-    return int(round(temperature_final))
+    return round(temperature_final)

45-45: Consider using logging instead of print() for library code.

Multiple print() calls throughout the module (lines 45, 225-229, 247-251, 267-271, 335, 370) make it difficult for library users to control output verbosity. Consider using the logging module:

import logging
logger = logging.getLogger(__name__)
# Then replace print(...) with logger.info(...)

This allows users to configure logging levels and handlers as needed.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98d66f5 and c6e19a2.

📒 Files selected for processing (2)
  • src/atomistics/calculators/lammps/melting.py
  • tests/test_melting_lammps.py
🧰 Additional context used
🧬 Code graph analysis (2)
tests/test_melting_lammps.py (1)
src/atomistics/calculators/lammps/melting.py (1)
  • estimate_melting_temperature_using_bisection_CNA (374-440)
src/atomistics/calculators/lammps/melting.py (1)
src/atomistics/calculators/lammps/libcalculator.py (2)
  • optimize_positions_and_volume_with_lammpslib (43-83)
  • calc_molecular_dynamics_npt_with_lammpslib (223-310)
🪛 Ruff (0.14.11)
src/atomistics/calculators/lammps/melting.py

286-286: Avoid specifying long messages outside the exception class

(TRY003)


440-440: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)

⏰ 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). (5)
  • GitHub Check: unittest_sphinxdft
  • GitHub Check: unittest_gpaw
  • GitHub Check: unittest_qe
  • GitHub Check: coverage
  • GitHub Check: notebooks
🔇 Additional comments (3)
tests/test_melting_lammps.py (1)

1-11: LGTM on the import guard pattern.

The try/except block properly handles the optional LAMMPS dependency, setting skip_lammps_test appropriately for conditional test execution.

src/atomistics/calculators/lammps/melting.py (2)

90-111: LGTM!

The diamond detection logic correctly compares "OTHER" counts between CNA and diamond descriptors to determine the appropriate analysis method.


114-135: LGTM!

Clean dispatch function for structure analysis.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 1 to 6
import operator
import numpy as np
from ase import Atoms
from random import random
from typing import Optional
from pandas import DataFrame
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical bug: incorrect import will cause AttributeError.

Line 4 imports the random() function, but line 407 calls random.randint(0, 99999). The function random doesn't have a .randint attribute—this will raise AttributeError: 'builtin_function_or_method' object has no attribute 'randint'.

🐛 Proposed fix
 import operator
 import numpy as np
 from ase import Atoms
-from random import random
+import random
 from typing import Optional
 from pandas import DataFrame
📝 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
import operator
import numpy as np
from ase import Atoms
from random import random
from typing import Optional
from pandas import DataFrame
import operator
import numpy as np
from ase import Atoms
import random
from typing import Optional
from pandas import DataFrame
🤖 Prompt for AI Agents
In `@src/atomistics/calculators/lammps/melting.py` around lines 1 - 6, The import
currently brings in the random() function (from random import random) but the
code later calls random.randint(0, 99999) (e.g., in functions/methods
referencing random.randint), causing AttributeError; change the import so the
module provides randint (either use import random or from random import randint)
and update any references if you choose the latter so calls to random.randint
become just randint; locate the import at the top of the file and replace it
accordingly to fix uses like random.randint.

Comment on lines 78 to 85
r_est = (target_number_of_atoms / len(input_structure)) ** (1 / 3)
candidates = np.array(
[max(1, int(np.floor(r_est))), int(np.round(r_est)), int(np.ceil(r_est))]
)
basis_lst = [input_structure.repeat([i, i, i]) for i in candidates]
basis = basis_lst[
np.argmin([np.abs(len(b) - target_number_of_atoms) for b in basis_lst])
]
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Edge case: potential for zero repetition factor.

When target_number_of_atoms < len(input_structure), r_est < 1, and np.round(r_est) could yield 0 (e.g., np.round(0.3) = 0), causing repeat([0, 0, 0]) to create an empty structure.

The max(1, ...) guard only applies to the floor value, not to round:

🛡️ Proposed defensive fix
     r_est = (target_number_of_atoms / len(input_structure)) ** (1 / 3)
     candidates = np.array(
-        [max(1, int(np.floor(r_est))), int(np.round(r_est)), int(np.ceil(r_est))]
+        [max(1, int(np.floor(r_est))), max(1, int(np.round(r_est))), max(1, int(np.ceil(r_est)))]
     )
📝 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
r_est = (target_number_of_atoms / len(input_structure)) ** (1 / 3)
candidates = np.array(
[max(1, int(np.floor(r_est))), int(np.round(r_est)), int(np.ceil(r_est))]
)
basis_lst = [input_structure.repeat([i, i, i]) for i in candidates]
basis = basis_lst[
np.argmin([np.abs(len(b) - target_number_of_atoms) for b in basis_lst])
]
r_est = (target_number_of_atoms / len(input_structure)) ** (1 / 3)
candidates = np.array(
[max(1, int(np.floor(r_est))), max(1, int(np.round(r_est))), max(1, int(np.ceil(r_est)))]
)
basis_lst = [input_structure.repeat([i, i, i]) for i in candidates]
basis = basis_lst[
np.argmin([np.abs(len(b) - target_number_of_atoms) for b in basis_lst])
]
🤖 Prompt for AI Agents
In `@src/atomistics/calculators/lammps/melting.py` around lines 78 - 85, The
repetition candidates can include zero when r_est < 1 because np.round(r_est)
may return 0, causing input_structure.repeat([0,0,0]) to produce an empty
structure; update the construction of candidates/basis_lst so every repeat
factor is at least 1 (e.g., apply max(1, ...) to the round and ceil results or
clip the entire candidates array to >=1) before building basis_lst and selecting
basis, referencing r_est, candidates, basis_lst, basis and
input_structure.repeat.

@jan-janssen
Copy link
Member Author

@prabhath-c there still seems to be an issue with the imports:

======================================================================
ERROR: test_estimate_melting_temperature (test_melting_lammps.TestLammpsMelting.test_estimate_melting_temperature)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/work/atomistics/atomistics/tests/test_melting_lammps.py", line 23, in test_estimate_melting_temperature
    melting_temp = estimate_melting_temperature_using_bisection_CNA(
        structure=input_structure,
    ...<8 lines>...
        log_file=None,
    )
  File "/home/runner/miniconda3/envs/test/lib/python3.14/site-packages/atomistics/calculators/lammps/melting.py", line 407, in estimate_melting_temperature_using_bisection_CNA
    seed = random.randint(0, 99999)
           ^^^^^^^^^^^^^^
AttributeError: 'builtin_function_or_method' object has no attribute 'randint'

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

Comment on lines 225 to 229
print(
f"Both structures have {key_max} above half distribution at temperatures {temperature_left} and {temperature_right}"
)
print("Increasing temperatures...")
print("-----------------------------------------------------")
Copy link
Member Author

Choose a reason for hiding this comment

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

The print messages should be optional, maybe add a debug flag to enable and disable the print statements.

Comment on lines 247 to 251
print(
f"Left structure has {key_max} above half distribution at temperature {temperature_left}, right structure below at temperature {temperature_right}"
)
print("Decreasing temperatures...")
print("-----------------------------------------------------")
Copy link
Member Author

Choose a reason for hiding this comment

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

Add a debug option for print statements

Comment on lines 267 to 271
print(
f"Both structures have {key_max} below half distribution at temperatures {temperature_left} and {temperature_right}"
)
print("Decreasing temperatures...")
print("-----------------------------------------------------")
Copy link
Member Author

Choose a reason for hiding this comment

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

Add a debug option for print statements

from ase.data import reference_states, atomic_numbers
import numpy as np
from ase import Atoms
from random import random
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
from random import random
import random

Atoms: The structure after MD simulation.
"""

print(f"Running NPT MD at T = {temperature} K")
Copy link
Member Author

Choose a reason for hiding this comment

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

Add debug option

Comment on lines 44 to 116
structure (pyiron_atomistics.structure.atoms.Atoms): The structure to analyze.
mode ("total"/"numeric"/"str"): Controls the style and level
of detail of the output.
- total : return number of atoms belonging to each structure
- numeric : return a per atom list of numbers- 0 for unknown,
1 fcc, 2 hcp, 3 bcc and 4 icosa
- str : return a per atom string of sructures
diamond (bool): Flag to either use the diamond structure detector or
the common neighbor analysis.
structure (Atoms): The structure to analyse.
Copy link
Member Author

Choose a reason for hiding this comment

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

Why was the docstring removed ?

)
temperature_step = temperature_right - temperature_left

print(f"Melting point estimated at T = {temperature_left} K")
Copy link
Member Author

Choose a reason for hiding this comment

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

Add debug option

@jan-janssen jan-janssen marked this pull request as draft January 16, 2026 08:13
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