From 54186c288beba76ae6d6994230e1916dad103d55 Mon Sep 17 00:00:00 2001 From: Matteo Becchi Date: Wed, 17 Dec 2025 10:44:02 +0100 Subject: [PATCH 1/4] Solving LENS issue with 2D systems. --- docs/source/_static/info_plot.png | Bin 141627 -> 141627 bytes src/dynsight/_internal/lens/lens.py | 4 +-- tests/lens/test_lens.py | 51 ++++++++++++++++++++++++++++ tests/systems/lens_2d_fbc.npy | Bin 0 -> 79328 bytes tests/systems/lens_2d_pbc.npy | Bin 0 -> 79328 bytes 5 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 tests/systems/lens_2d_fbc.npy create mode 100644 tests/systems/lens_2d_pbc.npy diff --git a/docs/source/_static/info_plot.png b/docs/source/_static/info_plot.png index 5a8c2a4c90b220dc2fab8431399ef67734facf4e..b426acf7f5ea4f45464cd830dfc75896ebd22e81 100644 GIT binary patch delta 49 zcmdmeiDUOAjtQP}7CH(UB_##LR{Hw6i6sR&`6W4-NqYH3>G}twOV2f?x27|0O=r5I F0RZUi6Py45 delta 49 zcmdmeiDUOAjtQP}<~j-)B_##LR{Hw6i6sR&`6W4-NqYH3>H0n!l|vfSThkf0rZZj9 F007;l60iUO diff --git a/src/dynsight/_internal/lens/lens.py b/src/dynsight/_internal/lens/lens.py index 30c7a8bb..29998854 100644 --- a/src/dynsight/_internal/lens/lens.py +++ b/src/dynsight/_internal/lens/lens.py @@ -265,7 +265,7 @@ def compute_lens( universe.trajectory[t1] pos_env1 = ag_env.positions.astype(np.float64) pos_cent1 = ag_cent.positions.astype(np.float64) - if respect_pbc and universe.trajectory.ts.dimensions is not None: + if universe.trajectory.ts.dimensions is not None: box = universe.trajectory.ts.dimensions[:3] else: coords = universe.atoms.positions @@ -284,7 +284,7 @@ def compute_lens( universe.trajectory[t2] pos_env2 = ag_env.positions.astype(np.float64) pos_cent2 = ag_cent.positions.astype(np.float64) - if respect_pbc and universe.trajectory.ts.dimensions is not None: + if universe.trajectory.ts.dimensions is not None: box = universe.trajectory.ts.dimensions[:3] else: coords = universe.atoms.positions diff --git a/tests/lens/test_lens.py b/tests/lens/test_lens.py index dc524fb9..aeac84af 100644 --- a/tests/lens/test_lens.py +++ b/tests/lens/test_lens.py @@ -7,9 +7,35 @@ import pytest from dynsight.trajectory import Trj +from dynsight.utilities import save_xyz_from_ndarray from .case_data import LENSCaseData +# ---------------- Fixtures ---------------- + + +@pytest.fixture +def trj_2d(tmp_path: Path) -> Trj: + """Return a Trj for a bidimensional system.""" + rng = np.random.default_rng(42) + coords = rng.random((100, 100, 3)) + coords[..., 2] = 0.0 # system is 2D + traj_path = tmp_path / "random_2d.xyz" + save_xyz_from_ndarray(traj_path, coords) + + trj = Trj.init_from_xyz(traj_path, dt=1.0) + for ts in trj.universe.trajectory: # Add box with thickness along z + coords = trj.universe.atoms.positions + mins = coords.min(axis=0) + maxs = coords.max(axis=0) + lengths = maxs - mins # Lx, Ly, Lz + lengths[2] = 0.5 + ts.dimensions = np.concatenate([lengths, np.array([90, 90, 90])]) + return trj + + +# ---------------- Tests ---------------- + def test_lens(case_data: LENSCaseData) -> None: original_dir = Path(__file__).resolve().parent @@ -35,3 +61,28 @@ def test_lens(case_data: LENSCaseData) -> None: ) exp_lens = np.load(expected_lens) assert np.allclose(exp_lens, test_lens.dataset, atol=1e-6) + + +def test_lens_2d(trj_2d: Trj) -> None: + """Test LENS on a 2D system.""" + original_dir = Path(__file__).resolve().parent + + test_lens_pbc = trj_2d.get_lens(r_cut=0.1, respect_pbc=True) + pbc_path = original_dir / "../systems/lens_2d_pbc.npy" + if not pbc_path.exists(): + np.save(pbc_path, test_lens_pbc.dataset) + pytest.fail( + "LENS test files were not present. They have been created." + ) + exp_lens = np.load(pbc_path) + assert np.allclose(exp_lens, test_lens_pbc.dataset) + + test_lens_fbc = trj_2d.get_lens(r_cut=0.1, respect_pbc=False) + fbc_path = original_dir / "../systems/lens_2d_fbc.npy" + if not fbc_path.exists(): + np.save(fbc_path, test_lens_fbc.dataset) + pytest.fail( + "LENS test files were not present. They have been created." + ) + exp_lens = np.load(fbc_path) + assert np.allclose(exp_lens, test_lens_fbc.dataset) diff --git a/tests/systems/lens_2d_fbc.npy b/tests/systems/lens_2d_fbc.npy new file mode 100644 index 0000000000000000000000000000000000000000..726cc665030b1a20c9938a39005254529c90073f GIT binary patch literal 79328 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 literal 0 HcmV?d00001 From 971b1cd07f0101acf76cacd4d970a041ba696eba Mon Sep 17 00:00:00 2001 From: Matteo Becchi Date: Wed, 17 Dec 2025 10:48:27 +0100 Subject: [PATCH 2/4] Solving neighbors_list issue with 2D systems. --- src/dynsight/_internal/lens/lens.py | 2 +- tests/lens/test_lens.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dynsight/_internal/lens/lens.py b/src/dynsight/_internal/lens/lens.py index 29998854..d3b8d26a 100644 --- a/src/dynsight/_internal/lens/lens.py +++ b/src/dynsight/_internal/lens/lens.py @@ -362,7 +362,7 @@ def _compute_frame_neighbors(frame_idx: int) -> list[AtomGroup]: pos_env = ag_env.positions.astype(np.float64) pos_cent = ag_centers.positions.astype(np.float64) - if respect_pbc and universe.trajectory.ts.dimensions is not None: + if universe.trajectory.ts.dimensions is not None: box = universe.trajectory.ts.dimensions[:3] else: coords = universe.atoms.positions diff --git a/tests/lens/test_lens.py b/tests/lens/test_lens.py index aeac84af..9d209fc7 100644 --- a/tests/lens/test_lens.py +++ b/tests/lens/test_lens.py @@ -64,10 +64,11 @@ def test_lens(case_data: LENSCaseData) -> None: def test_lens_2d(trj_2d: Trj) -> None: - """Test LENS on a 2D system.""" + """Test LENS and number of neighbors on a 2D system.""" original_dir = Path(__file__).resolve().parent test_lens_pbc = trj_2d.get_lens(r_cut=0.1, respect_pbc=True) + _ = trj_2d.get_coord_number(r_cut=0.1, respect_pbc=True) pbc_path = original_dir / "../systems/lens_2d_pbc.npy" if not pbc_path.exists(): np.save(pbc_path, test_lens_pbc.dataset) @@ -78,6 +79,7 @@ def test_lens_2d(trj_2d: Trj) -> None: assert np.allclose(exp_lens, test_lens_pbc.dataset) test_lens_fbc = trj_2d.get_lens(r_cut=0.1, respect_pbc=False) + _ = trj_2d.get_coord_number(r_cut=0.1, respect_pbc=False) fbc_path = original_dir / "../systems/lens_2d_fbc.npy" if not fbc_path.exists(): np.save(fbc_path, test_lens_fbc.dataset) From 318e12a412663a36a5197b430f6d1da594cdb53a Mon Sep 17 00:00:00 2001 From: Matteo Becchi Date: Wed, 17 Dec 2025 11:06:29 +0100 Subject: [PATCH 3/4] Hopefully silenced the annoying netCDF4 warning. --- src/dynsight/_internal/trajectory/trajectory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dynsight/_internal/trajectory/trajectory.py b/src/dynsight/_internal/trajectory/trajectory.py index 1df2da3c..a499f9ad 100644 --- a/src/dynsight/_internal/trajectory/trajectory.py +++ b/src/dynsight/_internal/trajectory/trajectory.py @@ -21,7 +21,8 @@ from dynsight.trajectory import Insight UNIVAR_DIM = 2 -logging.getLogger("MDAnalysis").setLevel(logging.ERROR) +logging.getLogger(__name__).addHandler(logging.NullHandler()) +logging.getLogger("MDAnalysis").addHandler(logging.NullHandler()) @dataclass(frozen=True) From 53f2456ec8e86735fcac8ee20621b85813c11e89 Mon Sep 17 00:00:00 2001 From: Matteo Becchi Date: Wed, 17 Dec 2025 11:39:40 +0100 Subject: [PATCH 4/4] Silencing also Universe initialization warnings. --- src/dynsight/_internal/trajectory/trajectory.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dynsight/_internal/trajectory/trajectory.py b/src/dynsight/_internal/trajectory/trajectory.py index a499f9ad..ac121860 100644 --- a/src/dynsight/_internal/trajectory/trajectory.py +++ b/src/dynsight/_internal/trajectory/trajectory.py @@ -21,8 +21,12 @@ from dynsight.trajectory import Insight UNIVAR_DIM = 2 -logging.getLogger(__name__).addHandler(logging.NullHandler()) +# Silence MDAnalysis INFO messages by default +logging.getLogger("MDAnalysis").setLevel(logging.WARNING) + +# Prevent log propagation unless user configures logging logging.getLogger("MDAnalysis").addHandler(logging.NullHandler()) +logging.getLogger(__name__).addHandler(logging.NullHandler()) @dataclass(frozen=True)