93 lines
5.3 KiB
Python
93 lines
5.3 KiB
Python
"""Versioned read-only DFG reanalysis; never overwrites a flow receipt."""
|
|
import argparse
|
|
import csv
|
|
import hashlib
|
|
import io
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
import zipfile
|
|
import numpy as np
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
import periodic as frozen
|
|
import periodic_v2 as v2
|
|
|
|
OFFICIAL_SHA256 = '08db59605b2f60efc512d2bbc79628ea5d1cc720c0986938a16920884a0215be'
|
|
PREFIX = 'logs/results_cc2d_bench2_referencenew/'
|
|
|
|
|
|
def sha(path): return hashlib.sha256(Path(path).read_bytes()).hexdigest()
|
|
|
|
|
|
def official_reference(path):
|
|
if sha(path) != OFFICIAL_SHA256: raise ValueError('Wrong official archive')
|
|
with zipfile.ZipFile(path) as z:
|
|
force = np.loadtxt(io.BytesIO(z.read(PREFIX+'bdforces_q2_lv6_dt4')))
|
|
pressure = np.loadtxt(io.BytesIO(z.read(PREFIX+'pointvalues_q2_lv6_dt4')))
|
|
tf=force[:,1]*10; tp=pressure[:,1]*10
|
|
if not np.isfinite(force).all() or not np.isfinite(pressure).all():
|
|
raise ValueError('Nonfinite official input')
|
|
if np.any(np.diff(tf)<=0) or np.any(np.diff(tp)<=0): raise ValueError('Official time order')
|
|
# Entire supplied interval, restricted only to shared force/pressure coverage.
|
|
mask=(tf>=tp[0])&(tf<=tp[-1]); force=force[mask]; tf=tf[mask]
|
|
dp=np.interp(tf,tp,pressure[:,6]-pressure[:,11])
|
|
rows=[dict(tstar=float(t),tstar_force=float(t),cd=float(f[3]),cl=float(f[4]),dp=float(p),
|
|
mass=1.,flux_in=1.,flux_out=1.) for t,f,p in zip(tf,force,dp)]
|
|
cycles=v2.extract(rows,min_time=0.)
|
|
if not cycles: raise ValueError('No complete official cycle')
|
|
ref=cycles[-1]
|
|
return ref,dict(archive_sha256=OFFICIAL_SHA256,selection='lv6/dt4 last complete ascending Cl=0 cycle in entire shared input coverage',
|
|
eligible_time=[float(tf[0]),float(tf[-1])],complete_cycles=len(cycles),
|
|
pressure='columns7 minus12, interpolated at original force times; physical time multiplied by10',
|
|
artificial_fields='mass/flux placeholders only; official archive is not used to certify conservation')
|
|
|
|
|
|
def main():
|
|
parser=argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--official-archive',type=Path,required=True)
|
|
parser.add_argument('--history',action='append',required=True,help='Unique label=CSV path; all cases reported')
|
|
parser.add_argument('--output',type=Path,required=True)
|
|
a=parser.parse_args()
|
|
if a.output.exists(): raise ValueError('Use a new output directory')
|
|
cases=[]; labels=set()
|
|
reference,reference_info=official_reference(a.official_archive)
|
|
old_ref_path=Path(frozen.__file__).parent/'data/dfg_re100/reference_cycle.json'
|
|
old_ref=json.loads(old_ref_path.read_text())
|
|
for item in a.history:
|
|
label,path=item.split('=',1); path=Path(path)
|
|
if label in labels: raise ValueError('Duplicate case label')
|
|
labels.add(label)
|
|
rows=[{k:float(v) for k,v in r.items()} for r in csv.DictReader(path.open())]
|
|
source_receipt_path=path.with_name('summary.json')
|
|
source_receipt=json.loads(source_receipt_path.read_text())
|
|
if rows != source_receipt.get('history'):
|
|
raise ValueError('CSV differs from original summary history')
|
|
old=frozen.assess(rows); new=v2.assess(rows)
|
|
for key in ('periodic', 'checks', 'cycles'):
|
|
if old[key] != source_receipt.get(key):
|
|
raise ValueError('Frozen reassessment differs from original summary')
|
|
old_comparison=frozen.compare(old['cycles'][-1],old_ref) if old['cycles'] else None
|
|
new_comparison=v2.compare(new['cycles'][-1],reference) if new['cycles'] else None
|
|
# This is assessment of recorded observables, not proof of unrecorded population health.
|
|
healthy=bool(rows and all(.5<=r['rho_min']<=r['rho_max']<=1.5 and 0<=r['max_mach']<=.2 for r in rows))
|
|
cases.append(dict(label=label,input_csv_sha256=sha(path),
|
|
original_summary_sha256=sha(source_receipt_path),
|
|
original_identity={k:source_receipt[k] for k in ('case','collision','observation_mode','numerical_source_commit','checkout','checkout_revision','mode','status') if k in source_receipt},
|
|
observations=len(rows),
|
|
recorded_end_tstar=rows[-1]['tstar'] if rows else None,recorded_observations_healthy=healthy,
|
|
frozen=old,frozen_reference=old_comparison,v2=new,v2_reference=new_comparison,
|
|
v2_recorded_observable_agreement=bool(healthy and new['periodic'] and new_comparison and new_comparison['reference_agreement'])))
|
|
a.output.mkdir(parents=True)
|
|
result=dict(assessment_version=v2.VERSION,original_receipts_unchanged=True,
|
|
phase='Every ascending Cl<0 to Cl>=0 crossing; linear interpolation, no suppression',
|
|
gates=v2.GATES,reference_gates=v2.REFERENCE_GATES,reference=reference,reference_provenance=reference_info,
|
|
code_sha256={Path(p).name:sha(p) for p in [__file__,v2.__file__,frozen.__file__]},
|
|
frozen_reference_sha256=sha(old_ref_path),cases=cases)
|
|
(a.output/'reanalysis.json').write_text(json.dumps(result,indent=2,allow_nan=False)+'\n')
|
|
print(json.dumps([dict(case=c['label'],old_periodic=c['frozen']['periodic'],
|
|
v2_checks=c['v2']['checks'],v2_reference=c['v2_reference'],
|
|
recorded_observable_agreement=c['v2_recorded_observable_agreement']) for c in cases],indent=2))
|
|
|
|
|
|
if __name__=='__main__': main()
|