Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ the candidate scope; it does not announce a release or assign a version DOI.
- The candidate adds `rotating_modal` and `campbell` routes with provisional
result contracts. They are not represented as stable API guarantees before
the 0.2.11 publication decision.
- Fixed Campbell execution-provenance hashing to resolve implementation files
from the installed wheel layout as well as a source checkout; no solver or
tracking calculation changed.
- Package metadata identifies 0.2.11; `CITATION.cff` continues to identify the
actually published 0.2.10 release. The package DOI URL uses the published
project concept DOI until a version DOI exists.
Expand Down
12 changes: 11 additions & 1 deletion src/solveur/core/analyses/campbell.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,15 @@ def _result_hash(result: RotatingModalResult) -> str:
return content_digest(payload.encode("utf-8"))


def _implementation_source_path(relative: str) -> Path:
"""Resolve a selected solveur source path in either checkout or wheel layout."""
prefix = "src/solveur/"
if not relative.startswith(prefix) or "\\" in relative or ".." in Path(relative).parts:
raise ValueError(f"Unexpected Campbell implementation path: {relative}")
package_root = Path(__file__).resolve().parents[2]
return package_root / relative[len(prefix) :]


def _execution_identity(
*, model_digest: str, validated: ValidatedCampbellInput, matrix_hash: str
) -> tuple[dict[str, Any], dict[str, Any]]:
Expand All @@ -275,8 +284,9 @@ def _execution_identity(
"src/solveur/core/model.py",
"src/solveur/post/campbell.py",
)

code_hashes = {
relative: hashlib.sha256((repository / relative).read_bytes()).hexdigest()
relative: hashlib.sha256(_implementation_source_path(relative).read_bytes()).hexdigest()
for relative in implementation_files
}
environment = {
Expand Down
16 changes: 16 additions & 0 deletions tests/unit/test_campbell.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
from __future__ import annotations

import json
from pathlib import Path

import numpy as np
import pytest

import solveur.core.analyses.campbell as campbell_analysis
from solveur.core.analyses.campbell import CampbellInputValidator, CampbellSolver
from solveur.core.analyses.settings import AnalysisSettings
from solveur.core.dofs import DOF_ORDER
Expand All @@ -18,6 +20,20 @@
from solveur.io.schema import JsonSchemaValidator


def test_implementation_hash_paths_support_installed_wheel_layout(tmp_path: Path, monkeypatch) -> None:
package_root = tmp_path / "venv" / "Lib" / "site-packages" / "solveur"
module_file = package_root / "core" / "analyses" / "campbell.py"
module_file.parent.mkdir(parents=True)
module_file.write_text("# installed wheel layout\n", encoding="utf-8")
monkeypatch.setattr(campbell_analysis, "__file__", str(module_file))

implementation = campbell_analysis._implementation_source_path(
"src/solveur/core/analyses/rotating_modal.py"
)

assert implementation == package_root / "core" / "analyses" / "rotating_modal.py"


def _beam_material() -> dict[str, float | str]:
radius = 0.05
area = np.pi * radius**2
Expand Down
Loading