Skip to content

fix(tf): close EwaldRecp TensorFlow sessions#5708

Open
wanghan-iapcm wants to merge 1 commit into
deepmodeling:masterfrom
wanghan-iapcm:fix-tf-ewald-session-close
Open

fix(tf): close EwaldRecp TensorFlow sessions#5708
wanghan-iapcm wants to merge 1 commit into
deepmodeling:masterfrom
wanghan-iapcm:fix-tf-ewald-session-close

Conversation

@wanghan-iapcm

@wanghan-iapcm wanghan-iapcm commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Problem

EwaldRecp creates a dedicated tf.Session in its constructor but exposes no way to close it (no close(), context-manager path, or destructor). DipoleChargeModifier creates and keeps an EwaldRecp instance without releasing it either. Creating and discarding these objects therefore leaks TensorFlow sessions and graph resources; long-running processes that repeatedly construct modifiers accumulate them until process exit.

Fix

Add close() (idempotent, guarded), context-manager support (__enter__/__exit__), and a defensive __del__ to EwaldRecp, and have DipoleChargeModifier.close() forward to the held EwaldRecp.

Test

test_ewald.py previously exercised only eval, never lifecycle. New tests assert that EwaldRecp.close() and context-manager exit release the session (a subsequent eval raises on the closed session), and that DipoleChargeModifier.close() forwards to its EwaldRecp.close(). All three fail on the current code with AttributeError (no such methods) and pass after the fix.

Fix #5685

Summary by CodeRabbit

  • New Features

    • Added safer resource handling for TensorFlow-based Ewald calculations, including explicit cleanup support and context-manager usage.
    • Added a cleanup method to the dipole-charge modifier so it can release related resources when finished.
  • Tests

    • Added coverage for cleanup behavior, including session shutdown and context-manager exit handling.
    • Added a test to ensure cleanup is forwarded correctly through the dipole-charge modifier.

EwaldRecp creates a dedicated tf.Session in its constructor but exposed no way
to close it, and DipoleChargeModifier holds an EwaldRecp without releasing it.
Repeatedly constructing and discarding these objects leaked TensorFlow sessions
and graph resources in long-running processes.

Add close(), context-manager support, and a defensive __del__ to EwaldRecp, and
have DipoleChargeModifier.close() forward to the EwaldRecp. Add tests that the
session is released after close()/context-manager exit and that the modifier
forwards close to its evaluator.

Fix deepmodeling#5685
@wanghan-iapcm wanghan-iapcm requested a review from njzjz July 2, 2026 01:32
@dosubot dosubot Bot added the bug label Jul 2, 2026
@github-actions github-actions Bot added the Python label Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds explicit TensorFlow session lifecycle management to EwaldRecp, including a close() method, context-manager support (__enter__/__exit__), and a __del__ finalizer. DipoleChargeModifier gains a close() method forwarding to its EwaldRecp instance. New tests validate both behaviors.

Changes

EwaldRecp session cleanup

Layer / File(s) Summary
EwaldRecp close/context-manager/destructor
deepmd/tf/infer/ewald_recp.py
Imports Self from typing_extensions and adds close(), __enter__, __exit__, and __del__ methods to close the stored TensorFlow session.
DipoleChargeModifier close forwarding and tests
deepmd/tf/modifier/dipole_charge.py, source/tests/tf/test_dipolecharge.py, source/tests/tf/test_ewald.py
Adds a close() method to DipoleChargeModifier forwarding to self.er.close() when present, and adds tests verifying close forwarding and that EwaldRecp sessions become unusable after close() or context-manager exit.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DipoleChargeModifier
  participant EwaldRecp
  participant TFSession

  DipoleChargeModifier->>DipoleChargeModifier: close()
  DipoleChargeModifier->>EwaldRecp: er.close()
  EwaldRecp->>TFSession: sess.close()
Loading

Related Issues: #5685

Suggested labels: enhancement, tests

Suggested reviewers: njzjz

🐰 A session once left open wide,
now closes gently, resources tied.
With close() and with, the rabbit's proud,
no leaking sessions in the TensorFlow cloud.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding session cleanup for EwaldRecp in TensorFlow.
Linked Issues check ✅ Passed The PR adds EwaldRecp.close(), modifier forwarding, context-manager support, and tests, matching #5685.
Out of Scope Changes check ✅ Passed The changes stay focused on TensorFlow session cleanup and related tests, with no unrelated features added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@deepmd/tf/modifier/dipole_charge.py`:
- Around line 108-113: The DipoleChargeModifier.close method only releases the
Ewald evaluator stored in self.er and leaves the TensorFlow session inherited
from DeepDipoleOld open. Update close() in DipoleChargeModifier to also guard
and close self.sess after closing er, so repeated create/discard cycles fully
release both sessions.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 91599b1b-6ed5-4ae4-bbea-09f84b087cc3

📥 Commits

Reviewing files that changed from the base of the PR and between 0f19772 and 6bc3a4d.

📒 Files selected for processing (4)
  • deepmd/tf/infer/ewald_recp.py
  • deepmd/tf/modifier/dipole_charge.py
  • source/tests/tf/test_dipolecharge.py
  • source/tests/tf/test_ewald.py

Comment on lines +108 to +113
def close(self) -> None:
"""Close the TensorFlow session held by the Ewald reciprocal evaluator."""
er = getattr(self, "er", None)
if er is not None:
er.close()

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether DeepDipole/DeepEval/BaseModifier define close() or otherwise
# manage self.sess/self.graph lifecycle.
rg -n -C3 'class DeepDipole\b|class DeepEval\b|class BaseModifier\b' --type=py
rg -n -C5 '\bdef close\b' deepmd/tf

Repository: deepmodeling/deepmd-kit

Length of output: 161


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant files and inspect the class definitions and close-related methods.
git ls-files 'deepmd/tf/modifier/dipole_charge.py' 'deepmd/tf/**/*.py' | sed -n '1,200p'
echo '--- dipole_charge.py ---'
cat -n deepmd/tf/modifier/dipole_charge.py | sed -n '1,220p'
echo '--- search close/session lifecycle ---'
rg -n -C4 '\bdef close\b|self\.sess|self\.graph|class DeepDipole\b|class DeepEval\b|class BaseModifier\b' deepmd/tf

Repository: deepmodeling/deepmd-kit

Length of output: 49678


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the base classes that provide the TF session/graph and whether they already
# define close() or other cleanup hooks.
sed -n '1,260p' deepmd/tf/infer/deep_dipole.py
echo '---'
sed -n '1,260p' deepmd/tf/modifier/base_modifier.py
echo '---'
sed -n '1,260p' deepmd/tf/infer/deep_eval.py | sed -n '1,220p'
echo '---'
rg -n -C3 '\bdef close\b|`@cached_property`\s+def sess\b|self\.sess\s*=|self\.graph\s*=' deepmd/tf/infer/deep_dipole.py deepmd/tf/infer/deep_eval.py deepmd/tf/modifier/base_modifier.py

Repository: deepmodeling/deepmd-kit

Length of output: 11583


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether DeepPot/DeepTensor already provide cleanup hooks that DipoleChargeModifier
# would inherit through BaseModifier -> DeepPot -> DeepEval/DeepTensor.
rg -n -C4 '\bdef close\b|class DeepPot\b|class DeepTensor\b|class DeepEvalBackend\b|class DeepEvalOld\b' deepmd/tf/infer/deep_pot.py deepmd/tf/infer/deep_tensor.py deepmd/tf/infer/deep_eval.py

Repository: deepmodeling/deepmd-kit

Length of output: 1079


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find any cleanup hooks on the inherited TF evaluation classes.
rg -n -C3 '\bdef close\b|class DeepPot\b|class DeepTensor\b|class DeepEvalOld\b|class DeepEvalBackend\b' \
  deepmd/tf/infer/deep_pot.py \
  deepmd/tf/infer/deep_tensor.py \
  deepmd/tf/infer/deep_eval.py \
  deepmd/tf/infer/deep_dipole.py

Repository: deepmodeling/deepmd-kit

Length of output: 900


Close the modifier's own TF session as well. close() currently releases only self.er; DipoleChargeModifier also keeps self.sess open from DeepDipoleOld, so repeated create/discard cycles still leak the main TensorFlow session. Add a guarded self.sess.close() here too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deepmd/tf/modifier/dipole_charge.py` around lines 108 - 113, The
DipoleChargeModifier.close method only releases the Ewald evaluator stored in
self.er and leaves the TensorFlow session inherited from DeepDipoleOld open.
Update close() in DipoleChargeModifier to also guard and close self.sess after
closing er, so repeated create/discard cycles fully release both sessions.

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.09%. Comparing base (0e5c170) to head (6bc3a4d).
⚠️ Report is 14 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5708      +/-   ##
==========================================
- Coverage   81.97%   81.09%   -0.88%     
==========================================
  Files         959      980      +21     
  Lines      105748   109369    +3621     
  Branches     4102     4207     +105     
==========================================
+ Hits        86684    88695    +2011     
- Misses      17573    19152    +1579     
- Partials     1491     1522      +31     

☔ View full report in Codecov by Harness.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

[Code scan] Close TensorFlow EwaldRecp sessions

1 participant