11 Commits

Author SHA1 Message Date
robrobo
e124506d10 fixed typo in logging output 2025-08-09 13:54:23 +02:00
robrobo
8169e76964 Merge branch 'main' into refactor_logging 2025-08-09 13:52:58 +02:00
65ac6e9143 Merge pull request 'using pbc_diff now in tetrahedral_order parameter calcul since reference positions will not be periodic images in the default use case' (ation,#5) from fix/tetrahedral_order_pbc into main
Reviewed-on: #5
2025-07-21 11:57:15 +00:00
robrobo
4047db209c using pbc_diff now in tetrahedral_order parameter calculation, since reference positions will not be periodic images in the default use case 2025-07-11 20:59:30 +02:00
robrobo
00043637e9 added a _seen set to avoid infinite recursions due to function arguments; also, applied pbc_diff to neighbors in tetrahedral order 2025-07-11 20:54:27 +02:00
robrobo
7585e598dc dedent before ast.parse for non-toplevel functions 2025-06-17 01:02:15 +02:00
robrobo
6d8b86c1ef extended checksum.strip_comments function to work with prefixed docstrings and other small features 2025-06-16 22:15:22 +02:00
robrobo
a2a0ae8d7b renamed logging to loggin_util to avoid circular import with python logging in some cases; added two raw strings to docstrings and fixed a sphinx syntax in one 2025-06-16 21:00:42 +02:00
90bd90a608 Merge pull request 'Added some ordering to checksums from FunctionType since these could depending on input fail to be deterministic' (#2) from fix_nondeterministic_checksum into main
Reviewed-on: #2
2025-06-16 18:45:48 +00:00
robrobo
67d3e70a66 Added some ordering to checksums from FunctionType since these could depending on input fail to be deterministic 2025-06-16 20:09:50 +02:00
c09549902a Added Robins adjustments for FEL 2024-05-27 14:27:09 +02:00
11 changed files with 84 additions and 37 deletions

View File

@@ -16,7 +16,7 @@ from . import reader
from . import system
from . import utils
from . import extra
from .logging import logger
from .logging_util import logger
def open(

View File

@@ -5,7 +5,7 @@ from typing import Optional, Callable, Iterable
import numpy as np
from .checksum import checksum
from .logging import logger
from .logging_util import logger
autosave_directory: Optional[str] = None
load_autosave_data = False

View File

@@ -1,9 +1,14 @@
import functools
import hashlib
from .logging import logger
from .logging_util import logger
from types import ModuleType, FunctionType
import inspect
from typing import Iterable
import ast
import io
import tokenize
import re
import textwrap
import numpy as np
@@ -28,19 +33,46 @@ def version(version_nr: int, calls: Iterable = ()):
return decorator
def strip_comments(s: str):
"""Strips comment lines and docstring from Python source string."""
o = ""
in_docstring = False
for l in s.split("\n"):
if l.strip().startswith(("#", '"', "'")) or in_docstring:
in_docstring = l.strip().startswith(('"""', "'''")) + in_docstring == 1
def strip_comments(source: str) -> str:
"""Removes docstrings, comments, and irrelevant whitespace from Python source code."""
# Step 1: Remove docstrings using AST
def remove_docstrings(node):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef, ast.Module)):
if (doc := ast.get_docstring(node, clean=False)):
first_stmt = node.body[0]
if isinstance(first_stmt, ast.Expr) and isinstance(first_stmt.value, ast.Constant):
node.body.pop(0) # Remove the docstring entirely
for child in ast.iter_child_nodes(node):
remove_docstrings(child)
tree = ast.parse(textwrap.dedent(source))
remove_docstrings(tree)
code_without_docstrings = ast.unparse(tree)
# Step 2: Remove comments using tokenize
tokens = tokenize.generate_tokens(io.StringIO(code_without_docstrings).readline)
result = []
last_lineno = -1
last_col = 0
for toknum, tokval, (srow, scol), (erow, ecol), line in tokens:
if toknum == tokenize.COMMENT:
continue
o += l + "\n"
return o
if srow > last_lineno:
last_col = 0
if scol > last_col:
result.append(" " * (scol - last_col))
result.append(tokval)
last_lineno, last_col = erow, ecol
code_no_comments = ''.join(result)
# Step 3: Remove empty lines (whitespace-only or truly blank)
return "\n".join([line for line in code_no_comments.splitlines() if line.strip() != ""])
def checksum(*args, csum=None):
def checksum(*args, csum=None, _seen=None):
"""
Calculate a checksum of any object, by sha1 hash.
@@ -60,7 +92,15 @@ def checksum(*args, csum=None):
csum = hashlib.sha1()
csum.update(str(SALT).encode())
if _seen is None:
_seen = set()
for arg in args:
obj_id = id(arg)
if obj_id in _seen:
continue
_seen.add(obj_id)
if hasattr(arg, "__checksum__"):
logger.debug("Checksum via __checksum__: %s", str(arg))
csum.update(str(arg.__checksum__()).encode())
@@ -73,17 +113,19 @@ def checksum(*args, csum=None):
elif isinstance(arg, FunctionType):
csum.update(strip_comments(inspect.getsource(arg)).encode())
c = inspect.getclosurevars(arg)
for v in {**c.nonlocals, **c.globals}.values():
merged = {**c.nonlocals, **c.globals}
for key in sorted(merged): # deterministic ordering
v = merged[key]
if v is not arg:
checksum(v, csum=csum)
checksum(v, csum=csum, _seen=_seen)
elif isinstance(arg, functools.partial):
logger.debug("Checksum via partial for %s", str(arg))
checksum(arg.func, csum=csum)
checksum(arg.func, csum=csum, _seen=_seen)
for x in arg.args:
checksum(x, csum=csum)
checksum(x, csum=csum, _seen=_seen)
for k in sorted(arg.keywords.keys()):
csum.update(k.encode())
checksum(arg.keywords[k], csum=csum)
checksum(arg.keywords[k], csum=csum, _seen=_seen)
elif isinstance(arg, np.ndarray):
csum.update(arg.tobytes())
else:

View File

@@ -1,6 +1,6 @@
from functools import partial, wraps
from copy import copy
from .logging import logger
from .logging_util import logger
from typing import Optional, Callable, List, Tuple
import numpy as np

View File

@@ -431,9 +431,9 @@ def non_gaussian_parameter(
trajectory: Coordinates = None,
axis: str = "all",
) -> float:
"""
r"""
Calculate the non-Gaussian parameter.
..math:
.. math:
\alpha_2 (t) =
\frac{3}{5}\frac{\langle r_i^4(t)\rangle}{\langle r_i^2(t)\rangle^2} - 1
"""

View File

@@ -182,10 +182,10 @@ def tetrahedral_order(
)
# Connection vectors
neighbors_1 -= atoms
neighbors_2 -= atoms
neighbors_3 -= atoms
neighbors_4 -= atoms
neighbors_1 = pbc_diff(neighbors_1, atoms, box=atoms.box)
neighbors_2 = pbc_diff(neighbors_2, atoms, box=atoms.box)
neighbors_3 = pbc_diff(neighbors_3, atoms, box=atoms.box)
neighbors_4 = pbc_diff(neighbors_4, atoms, box=atoms.box)
# Normed Connection vectors
neighbors_1 /= np.linalg.norm(neighbors_1, axis=-1).reshape(-1, 1)

View File

@@ -4,7 +4,6 @@ from typing import Optional
import numpy as np
from numpy.typing import ArrayLike, NDArray
from numpy.polynomial.polynomial import Polynomial as Poly
import math
from scipy.spatial import KDTree
import pandas as pd
import multiprocessing as mp
@@ -49,7 +48,7 @@ def _pbc_points_reduced(
def _build_tree(points, box, r_max, pore_geometry):
if np.all(np.diag(np.diag(box)) == box):
tree = KDTree(points, boxsize=box)
tree = KDTree(points % box, boxsize=box)
points_pbc_index = None
else:
points_pbc, points_pbc_index = _pbc_points_reduced(
@@ -79,8 +78,7 @@ def occupation_matrix(
z_bins = np.arange(0, box[2][2] + edge_length, edge_length)
bins = [x_bins, y_bins, z_bins]
# Trajectory is split for parallel computing
size = math.ceil(len(frame_indices) / nodes)
indices = [frame_indices[i : i + size] for i in range(0, len(frame_indices), size)]
indices = np.array_split(frame_indices, nodes)
pool = mp.Pool(nodes)
results = pool.map(
partial(_calc_histogram, trajectory=trajectory, bins=bins), indices
@@ -274,7 +272,11 @@ def distance_resolved_energies(
def find_energy_maxima(
energy_df: pd.DataFrame, r_min: float, r_max: float
energy_df: pd.DataFrame,
r_min: float,
r_max: float,
r_eval: float = None,
degree: int = 2,
) -> pd.DataFrame:
distances = []
energies = []
@@ -283,6 +285,9 @@ def find_energy_maxima(
x = np.array(data_d["r"])
y = np.array(data_d["energy"])
mask = (x >= r_min) * (x <= r_max)
p3 = Poly.fit(x[mask], y[mask], deg=2)
energies.append(np.max(p3(np.linspace(r_min, r_max, 1000))))
p3 = Poly.fit(x[mask], y[mask], deg=degree)
if r_eval is None:
energies.append(np.max(p3(np.linspace(r_min, r_max, 1000))))
else:
energies.append(p3(r_eval))
return pd.DataFrame({"d": distances, "energy": energies})

View File

@@ -7,7 +7,7 @@ from numpy.typing import ArrayLike, NDArray
from itertools import product
from .logging import logger
from .logging_util import logger
if TYPE_CHECKING:
from mdevaluate.coordinates import CoordinateFrame

View File

@@ -19,13 +19,13 @@ import MDAnalysis
from scipy import sparse
from .checksum import checksum
from .logging import logger
from .logging_util import logger
from . import atoms
from .coordinates import Coordinates
CSR_ATTRS = ("data", "indices", "indptr")
NOJUMP_MAGIC = 2016
Group_RE = re.compile("\[ ([-+\w]+) \]")
Group_RE = re.compile(r"\[ ([-+\w]+) \]")
class NojumpError(Exception):
@@ -275,7 +275,7 @@ def load_nojump_matrices(reader: BaseReader):
"Loaded Nojump matrices: {}".format(nojump_load_filename(reader))
)
else:
logger.info("Invlaid Nojump Data: {}".format(nojump_load_filename(reader)))
logger.info("Invalid Nojump Data: {}".format(nojump_load_filename(reader)))
except KeyError:
logger.info("Removing zip-File: %s", zipname)
os.remove(nojump_load_filename(reader))

View File

@@ -14,7 +14,7 @@ from scipy.ndimage import uniform_filter1d
from scipy.interpolate import interp1d
from scipy.optimize import curve_fit
from .logging import logger
from .logging_util import logger
from .functions import kww, kww_1e