feat: adopt lumped mass and reduced-coordinate shell HHT
Integrate Hex8 mass verification and fixed-null-basis shell dynamics with numerical and physics handoff evidence. Preserve interrupted reference and full-validation status; full integration validation is deferred until after performance optimization.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""Compare original Hex8 CSVs with official Solid HDF5; never launch a solver.
|
||||
"""Compare original Hex8 CSV/TXT results with Solid HDF5; never launch a solver.
|
||||
|
||||
This entry point is intentionally restricted to reference/hexa's fixed original
|
||||
models. Identity/schema checks precede finite checks and the common tolerance.
|
||||
@@ -43,6 +43,7 @@ SOURCE_SHA256 = {
|
||||
"hexa_dynnonlinear.inp": "1685f86f844e8020bf46e1747b949699ac9aff0d7570bd21ec95109399365ff9",
|
||||
"hexa_dynnonlinear_U.csv": "f5832a24577f54838bb49dec86a0dc9668cf43e78953f718a9d3635e0f574226",
|
||||
"hexa_freq.inp": "42cbcec2a8b738f8b6d2c1e13114b8840dccdde6fc3e6f8a43a3272e85d82347",
|
||||
"hexa_freq.txt": "3d2dacbee13cd2cc8d3f7c1a38210611a72de41d5e31d169e636bf40e00d4370",
|
||||
"hexa_linearstatic.inp": "67183909c5dc962a4d4b65ffb81f7bd65b72e89a5d015c8369a8e0226347a647",
|
||||
"hexa_linearstatic_RF.csv": "3e04e71ec075ffef098cf4870d77f9c400557fccb375369da8bf67cf67c1e36b",
|
||||
"hexa_linearstatic_S.csv": "8840a502b9d28a91e5685349301a56fed37d2ac62327b71304716b714db577e8",
|
||||
@@ -293,8 +294,39 @@ def evaluate_family(identity, result, reference):
|
||||
"diagnostic_code": "zero-reference-scale-nonzero-error" if not scale and not rows_passed else None}
|
||||
|
||||
|
||||
def read_modal_reference(path: Path):
|
||||
"""Read the supplied hexa_freq.txt Value=lambda, Freq=cycles/time format.
|
||||
|
||||
This artifact has no step field: its fixed case is bound to the sole Step-1
|
||||
of the hash-checked hexa_freq.inp. Freq is finite auxiliary data; comparison
|
||||
uses the printed Value directly, without frequency conversion or rounding.
|
||||
"""
|
||||
mapping = {}
|
||||
for line in path.read_text(encoding="utf-8-sig").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
match = re.fullmatch(
|
||||
r"\s*Mode\s+([0-9]+):\s+Value\s*=\s*(\S+)\s+Freq\s*=\s*(\S+)\s+\(cycles/time\)\s*", line)
|
||||
require(match is not None, "wrong-modal-reference-format", line)
|
||||
mode = int(match[1])
|
||||
require(mode in range(1, 11), "wrong-modal-mode", str(mode))
|
||||
require(mode not in mapping, "duplicate-modal-mode", str(mode))
|
||||
mapping[mode] = (match[2], match[3])
|
||||
require(set(mapping) == set(range(1, 11)), "modal-mode-inventory", "Lowest ten modes required.")
|
||||
rows = []
|
||||
for mode in range(1, 11):
|
||||
value, frequency = (float(token) for token in mapping[mode])
|
||||
require(math.isfinite(value) and math.isfinite(frequency), "nonfinite-modal-reference", str(mode))
|
||||
require(value >= 0 and frequency >= 0, "invalid-modal-reference-value", str(mode))
|
||||
rows.append({"step": STEP, "mode": mode, "quantity": "eigenvalue",
|
||||
"unit_dimensions": "1/time^2", "value": value})
|
||||
require(all(a["value"] <= b["value"] for a, b in zip(rows, rows[1:])),
|
||||
"wrong-modal-order", str(path))
|
||||
return rows
|
||||
|
||||
|
||||
def compare_modal(reference, result, quantity: str, unit: str):
|
||||
"""Typed modal rows, not an invented format for the absent original CSV.
|
||||
"""Compare explicitly typed modal rows using the common tolerance.
|
||||
|
||||
Each row explicitly supplies step, mode, quantity, unit_dimensions and value.
|
||||
Callers must obtain those semantics from a genuine supplied artifact first.
|
||||
@@ -316,7 +348,8 @@ def compare_modal(reference, result, quantity: str, unit: str):
|
||||
require(set(mapping) == set(range(1, 11)), "modal-mode-inventory", "Lowest ten modes required.")
|
||||
inventories.append(mapping)
|
||||
families = [evaluate_family(
|
||||
{"case": "hexa_freq", "step": STEP, "mode": mode, "quantity": quantity,
|
||||
{"identity": f"hexa_freq:{STEP}:mode-{mode}:{quantity}",
|
||||
"case": "hexa_freq", "step": STEP, "mode": mode, "quantity": quantity,
|
||||
"unit_dimensions": unit, "rows": [{"mode": mode, "component": quantity}]},
|
||||
[inventories[1][mode]], [inventories[0][mode]]) for mode in range(1, 11)]
|
||||
return {"families": families, "passed": all(family["passed"] for family in families)}
|
||||
@@ -382,7 +415,7 @@ def read_results(path: Path, model):
|
||||
for name, expected in {
|
||||
"feature_id": "hex8-structural-analysis", "analysis_procedure": CASES[case],
|
||||
"element_formulation": "FESA-Hex8", "integration_rule": "gauss-2x2x2",
|
||||
"mass_formulation": "consistent-reference", "source_step_name": STEP,
|
||||
"mass_formulation": "lumped-reference", "source_step_name": STEP,
|
||||
"unit_system_label": "user-consistent-unspecified",
|
||||
"coordinate_convention": "global-cartesian; reference-strain; current-cauchy-stress",
|
||||
"component_order": "node-major; UX,UY,UZ",
|
||||
@@ -635,6 +668,9 @@ def compare(case: str, input_path: Path, results_path: Path, reference_directory
|
||||
quantities = ["U"] if case.startswith("hexa_dyn") else (["U", "RF", "S"] if case != "hexa_freq" else [])
|
||||
paths = {"input": input_path, "results": results_path}
|
||||
paths.update({quantity: reference_directory / f"{case}_{quantity}.csv" for quantity in quantities})
|
||||
modal_path = reference_directory / "hexa_freq.txt"
|
||||
if case == "hexa_freq" and modal_path.exists():
|
||||
paths["Value"] = modal_path
|
||||
for name, path in paths.items():
|
||||
report["identities"][name] = {"path": lexical_absolute(path), "sha256": sha256(path)}
|
||||
model = input_inventory(input_path, case)
|
||||
@@ -660,6 +696,11 @@ def compare(case: str, input_path: Path, results_path: Path, reference_directory
|
||||
"record_path": lexical_absolute(execution_record),
|
||||
"record_sha256": sha256(execution_record)}
|
||||
records = {}
|
||||
modal_reference = None
|
||||
if "Value" in paths:
|
||||
require(report["identities"]["Value"]["sha256"] == SOURCE_SHA256["hexa_freq.txt"],
|
||||
"reference-hash-mismatch", str(modal_path))
|
||||
modal_reference = read_modal_reference(modal_path)
|
||||
for quantity in quantities:
|
||||
require(report["identities"][quantity]["sha256"] == SOURCE_SHA256[f"{case}_{quantity}.csv"],
|
||||
"reference-hash-mismatch", str(paths[quantity]))
|
||||
@@ -673,8 +714,25 @@ def compare(case: str, input_path: Path, results_path: Path, reference_directory
|
||||
# Internal maps are useful to validation but unnecessary in the public report.
|
||||
del report["precheck"]["result_node_map"], report["precheck"]["result_element_map"]
|
||||
if case == "hexa_freq":
|
||||
report["verdict"] = "not-run"
|
||||
report["reason"] = "Original modal Value reference is absent; no format/quantity/unit assumption made."
|
||||
if modal_reference is None:
|
||||
report["verdict"] = "not-run"
|
||||
report["reason"] = "Original modal Value reference is absent; no format/quantity/unit assumption made."
|
||||
return report
|
||||
actual = [{"step": STEP, "mode": int(row["mode_id"]), "quantity": "eigenvalue",
|
||||
"unit_dimensions": "1/time^2", "value": float(row["eigenvalue"])}
|
||||
for row in result["modal"]["modes"]]
|
||||
modal = compare_modal(modal_reference, actual, "eigenvalue", "1/time^2")
|
||||
report["precheck"]["reference_row_counts"]["Value"] = len(modal_reference)
|
||||
report["modal_reference"] = {"format": "Mode n: Value = lambda Freq = f (cycles/time)",
|
||||
"step_binding": "sole Step-1 of approved hexa_freq.inp",
|
||||
"quantity": "eigenvalue", "unit_dimensions": "1/time^2",
|
||||
"frequency_role": "finite auxiliary; no conversion"}
|
||||
report["families"] = modal["families"]
|
||||
for family in report["families"]:
|
||||
family["reference_sha256"] = report["identities"]["Value"]["sha256"]
|
||||
report["blocking_failures"] = [f["identity"] for f in modal["families"] if not f["passed"]]
|
||||
report["blocking_passed"] = modal["passed"]
|
||||
report["verdict"] = "passed" if modal["passed"] else "failed"
|
||||
return report
|
||||
families = []
|
||||
for increment in sorted(result["frames"]):
|
||||
|
||||
Reference in New Issue
Block a user