Skip to content

pi-thon 3.14 fixes - #75

Merged
mimakaev merged 16 commits into
masterfrom
mi/3.14_fixes
Aug 9, 2026
Merged

pi-thon 3.14 fixes#75
mimakaev merged 16 commits into
masterfrom
mi/3.14_fixes

Conversation

@mimakaev

@mimakaev mimakaev commented Oct 26, 2025

Copy link
Copy Markdown
Collaborator

Description

This PR contains fixes for the python 3.14 switching from fork to forkserver multiprocessing method, addresses some deprecation warnings, and reformats things with black.

  • Changed CI to include 3.14 and two latest versions of MacOS
  • black and isort on the codebase
  • Switched contactmaps to be compatible with spawn and forkserver multiprocessing (latest default in 3.14)
  • removed confusing test for an old starting conformation generator
  • Addressed all flake8 issues and all valid VSCode warnings such as possibly unbound variables
  • Deprecated some unnecessary functions, like streaming ndarray agg
  • redid imports from simtk.unit due to dynamic module nature (pylance errors)

The PR also revisits topology preserving simulation, and adds a forcekit and a tested example (using claude fable model).

PR Checklist

  • [] apply "black" to the whole codebase (black .)
  • [] apply isort to the codebase (isort .)
  • [] fun flake8 and try to resolve all the issues (work in progress!)

Copilot AI left a comment

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.

Pull Request Overview

This PR updates the polychrom codebase for Python 3.14 compatibility, focusing on multiprocessing method changes (fork → forkserver), addressing deprecation warnings, and applying code formatting with black/isort.

Key Changes:

  • Updated CI/CD to test against Python 3.14 and recent macOS versions
  • Refactored contactmaps module for spawn/forkserver multiprocessing compatibility (lambda functions → named functions)
  • Fixed NumPy deprecation (np.in1dnp.isin) and improved type checking patterns
  • Applied consistent code formatting across the codebase

Reviewed Changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
.github/workflows/pythonapp.yml Added Python 3.14 and macOS testing matrix
polychrom/contactmaps.py Refactored for multiprocessing compatibility: moved lambdas to module-level functions, added default function parameters
tests/test_contactmaps.py Replaced lambda with named load_function for spawn/forkserver compatibility; added module-level test data
polychrom/forces.py Updated cylindrical_confinement logic and type hints; contains a bug with bottom=0 handling
polychrom/forcekits.py Added explicit error for unsupported nonbonded forces (potential breaking change)
tests/test_polymer_analyses.py Fixed NumPy deprecation: np.in1dnp.isin
tests/test_io.py Updated to use new load_URI API instead of deprecated fetch_block
polychrom/polymer_analyses.py Added TYPE_CHECKING pattern for better Cython import handling
polychrom/param_units.py Reformatted docstrings with black (r""" prefix)
polychrom/hdf5_format.py Changed type() to isinstance() for better practices
polychrom/legacy/forces.py Reordered imports and changed to isinstance()
Various files Import reordering (isort), spacing fixes, removed extra blank lines (black formatting)
tests/test_configurations.py Removed entire test file for old starting conformation generator
Examples Import reordering and formatting improvements

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

Comment thread polychrom/forces.py Outdated
bottom = 0 if bottom is True else bottom

if bottom is not None:
if bottom:

Copilot AI Nov 11, 2025

Copy link

Choose a reason for hiding this comment

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

The logic change here introduces a bug. When bottom=0 is passed, the condition if bottom: (line 734) will evaluate to False because 0 is falsy in Python, causing the code to use the wrong branch.

The original code correctly used if bottom is not None: which would handle bottom=0 properly. The fix should be:

bottom = 0 if bottom is True else bottom

if bottom is not None:
    # ... existing code for bottom confinement
Suggested change
if bottom:
if bottom is not None:

Copilot uses AI. Check for mistakes.
Comment thread polychrom/forcekits.py Outdated
@@ -1 +1 @@
from polychrom.polymerutils import * # noqa: F403
from polychrom.polymerutils import * # noqa: F403

Copilot AI Nov 11, 2025

Copy link

Choose a reason for hiding this comment

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

Import pollutes the enclosing namespace, as the imported module polychrom.polymerutils does not define 'all'.

Suggested change
from polychrom.polymerutils import * # noqa: F403
# TODO: Replace the following line with explicit imports of only the required names.
# For example: from polychrom.polymerutils import foo, bar
# from polychrom.polymerutils import * # noqa: F403

Copilot uses AI. Check for mistakes.
Comment thread polychrom/simulation.py Outdated
@mimakaev

Copy link
Copy Markdown
Collaborator Author

unused imports and other things will be addressed in a next PR

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

polychrom/param_units.py:189

  • guess_bondWiggleDistance doesn’t use self but is now an instance method, which is a silent breaking change for any code calling it as a class/static utility (e.g. SimulationParams.guess_bondWiggleDistance(...)). Consider making it a @staticmethod again (or accepting both calling styles) to preserve the previous API.
    def guess_bondWiggleDistance(self, L0, b, mean_linker_length, a=None):
        """Return bond wiggle distance based on the amount of DNA per bead (L0), the
        Kuhn length (b) in basepairs, and the mean linker length in basepairs, and the
        expected radius of a monomer in nanometers (a)."""
        L0_nm = L0 / (1 + 146 / mean_linker_length) * 0.34
        b_nm = b / (1 + 146 / mean_linker_length) * 0.34
        if a is None:
            a = b_nm
        return np.sqrt(2 * L0_nm * b_nm / 3) / a

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

Comment thread tests/test_starting_conformations.py Outdated
Comment thread tests/test_hdf5_format.py Outdated
Comment thread polychrom/hdf5_format.py
Comment on lines 256 to 259
for file in files:
try:
h5py.File(file, "r")
except Exception:

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

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

list_URIs opens each HDF5 file with h5py.File(file, "r") but never closes it. Use a context manager to ensure file handles are released (or explicitly close) to avoid leaking descriptors during large directory scans.

Copilot uses AI. Check for mistakes.
Comment thread polychrom/simulation.py Outdated
Comment on lines 97 to 98
import openmm # if this fails, update openmm

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

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

simulation.py now hard-imports openmm with no fallback to simtk.openmm. Since OpenMM isn’t listed in requirements.txt and other modules still support the legacy import path, this can break environments that only have simtk.openmm installed. Consider restoring the previous try/except import fallback (or updating installation requirements consistently across the project).

Suggested change
import openmm # if this fails, update openmm
try:
import openmm # preferred OpenMM namespace
except ImportError: # fall back to legacy OpenMM package layout
from simtk import openmm # type: ignore[no-redef]

Copilot uses AI. Check for mistakes.
Comment thread polychrom/polymer_analyses.py Outdated
Comment on lines +616 to +620
except ImportError:
warnings.warn(
"C++ simplification module not available. " "Please compile the Cython extensions.", RuntimeWarning
)
return data

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

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

This only catches ImportError, but if _polymer_math failed to import at module import time (the top-level try/except does pass), calling _polymer_math.simplifyPolymer(...) will raise NameError, not ImportError, and won’t be handled. Consider setting _polymer_math = None when import fails and/or catching NameError here so the intended warning + fallback path works.

Copilot uses AI. Check for mistakes.
mimakaev and others added 6 commits February 18, 2026 12:08
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ogy code

- hdf5_format: write strings/lists-of-strings as original Python objects
  (vlen UTF-8), not numpy S/U conversions - fixes TypeError crash on lists
  of strings and the 64KB fixed-length attribute limit on older HDF5
- hdf5_format: continue_trajectory now compares block numbers, not array
  positions - no longer destroys blocks in non-contiguous trajectories;
  re-buffers all collateral blocks from deleted files
- hdf5_format: list_URIs(read_error=False) warns when skipping unreadable
  files instead of silently truncating the trajectory
- _polymer_math C: fix stale-N bug in _simplifyCpp (sweeps 2+ read garbage
  tail points and lost the ring-closure edge; output now contains only real
  points and topology is verified preserved against an independent
  Alexander-invariant implementation for 3_1/4_1/5_1/7_1 and lattice unknots)
- _polymer_math C: getLinkingNumber returned -2x the true linking number
  (signed crossing sum, never halved, inverted sign); now matches the Gauss
  integral exactly (Hopf = +-1)
- polymer_analyses: simplifyPolymer no longer swallows its own missing-
  extension ImportError (was silently returning unsimplified data);
  single _require_polymer_math() helper shared by all three topology functions
- polymer_analyses: restore ndarray_groupby_aggregate and
  streaming_ndarray_agg (public documented API, deleted without deprecation)
- contactmaps: exceptionsToIgnore=None no longer crashes with
  TypeError(tuple(None)) in findN/iterators; binnedContactMap default fixed
- forcekits: raise ValueError when except_bonds is requested but the
  nonbonded force supports neither exceptions nor exclusions (was
  print-and-continue with silently wrong physics)
- simulation: do_block docstring no longer promises a steps default that
  does not exist
- CLAUDE.md: fix doBlock -> do_block, correct polymerutils.load() claims
- tests: regression tests for string roundtrip, gapped continue_trajectory;
  polymer_math tests now assert exact invariants (|lk|=1 for Hopf, stick
  number bound for trefoil, no garbage rows) instead of codifying the bugs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- forcekits: add grosberg_polymer_chains - the Kremer-Grest/Halverson force
  set (FENE + WCA + bending) preconfigured correctly, most importantly with
  except_bonds=False: the polymer_chains default of excluding bonded pairs
  from the nonbonded force silently breaks FENE (its minimum is at r=0; the
  0.97 sigma bond length comes from the WCA balance). Docstring carries the
  validated numerics (dt <= 82 fs langevinMiddle, collision_rate 0.079/ps,
  fixed timestep only, warmup protocol). No trunc parameter on purpose:
  truncated repulsion + FENE collapses and NaNs.
- forces: correct grosberg_* docstrings - grosberg_polymer_bonds claimed a
  built-in repulsion it does not have; grosberg_angle claimed k=1.5
  "maximizes entanglement length" when it reduces Ne to ~28 (that being the
  point); grosberg_repulsive_force trunc guidance replaced with measured
  crossing rates (trunc=3 is leaky, only trunc=None preserves topology).
- polymer_analyses: add alexander_invariants(ring) - exact knot detection:
  |Alexander(-1)| and odd |Alexander(-2)| via integer Bareiss determinants
  on a generic projection, with degenerate-projection rejection,
  clearance-scaled perturbation, and two-projection agreement. Unknot=(1,1),
  trefoil=(3,7), 4_1=(5,11), 5_1=(5,31). Uses the (fixed) C simplifyPolymer
  for pre-reduction. Tested against the knot table and grow_cubic unknots.
- examples/topologyPreservingRingMelt: executed notebook showcasing the
  whole workflow: unentangled ring-melt construction, parameter table with
  validated timestep/friction limits, simulation via the forcekit,
  knot + periodic-image-aware linking verification, and a crossable
  (trunc=1.5, harmonic bonds) positive control demonstrating detection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@review-notebook-app

Copy link
Copy Markdown

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@mimakaev
mimakaev merged commit 1e9ac0f into master Aug 9, 2026
4 checks passed
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