Files
CelerisLab/tests/validation/cycle_v2/reanalyse.py
T

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()