#!/usr/bin/env python3 """Audit coupled-trial saved vectors and transactions, not physical convergence. A retained correction can still fail Armijo. Each alpha is an independent frozen trial, not one step in a nonlinear trajectory. Saved J actions are checked by vector algebra; this checker does not perform another FEM operator application. """ import argparse import json import math from pathlib import Path from check_nonlinear_step import EPS, close, digest, dot, layout_for, minus, norm, rows, sanitize, vector P = 'coupled_trial_' STATUS = ('not_attempted','original_accepted','budget_exhausted','kept','linear_failure', 'norm_limit','non_finite','inadmissible','not_improved') GRAVITY_VALUES = {'gravity_gradient','gravity_potential'} GRAVITY_ROWS = {'gravity_gradient_relation','poisson_balance'} BASE = {'metadata.txt'} | {P+n+'.csv' for n in ('summary','layout','preparations','trials','blocks','physical')} | { P+n+'.txt' for n in ('base_normalized','base_physical','base_residual','state_factors','residual_factors', 'direction','base_action','base_defect','restored_residual','restored_action')} def near_vector(a,b,scale): return len(a)==len(b) and norm(minus(a,b)) <= 1024*EPS*max(scale,1.e-28) def finite_ratio(numerator,denominator): if not math.isfinite(numerator) or not math.isfinite(denominator) or denominator==0: return None result=numerator/denominator return result if math.isfinite(result) else None def correction_mechanism(base,direction,alpha,prediction,before_residual,corrected_state, corrected_residual,correction,rhs,action): """Describe a saved correction; large remainders are observations, not failed audits. The candidate Jacobian action gives R_new = (J_candidate c - rhs) + E, where E is the correction's own Taylor remainder. Improving merit does not imply that either term or the affine-target remainder becomes smaller. """ cn,pn,rn=norm(correction),norm(direction),norm(rhs) displacement=minus(corrected_state,base) before=minus(before_residual,prediction) after=minus(corrected_residual,prediction) defect=minus(action,rhs) own_taylor=minus(minus(corrected_residual,before_residual),action) en,dn=norm(own_taylor),norm(defect) reconstruction=[v+w for v,w in zip(defect,own_taylor)] return sanitize({ 'correction_direction_cosine':finite_ratio(dot(correction,direction),cn*pn), 'correction_over_rhs_norm':finite_ratio(cn,rn), 'correction_over_alpha_squared':finite_ratio(cn,alpha*alpha), 'corrected_displacement_norm':norm(displacement), 'corrected_displacement_over_outer_step_norm':finite_ratio(norm(displacement),alpha*pn), 'effective_direction_projection_t':finite_ratio(dot(displacement,direction),pn*pn), 'remainder_before_norm':norm(before), 'remainder_after_norm':norm(after), 'verified_linear_defect_norm':dn, 'own_taylor_remainder_norm':en, 'own_taylor_over_correction_squared':finite_ratio(en,cn*cn), 'own_taylor_over_verified_defect':finite_ratio(en,dn), 'linear_defect_dot_own_taylor':dot(defect,own_taylor), 'remainder_decomposition_error_norm':norm(minus(after,reconstruction)), 'scope':'Saved candidate-J vector algebra; normalized coefficient norms, with no new FEM action or asymptotic-order claim.'}) def inspect_case(folder): try: return inspect_case_artifacts(Path(folder)) except (OSError,ValueError,KeyError,TypeError,IndexError,OverflowError,ZeroDivisionError) as error: return {'passed':False,'checks':[{'check':'complete_readable_artifacts','passed':False,'error':str(error)}], 'observations_valid':False,'required_artifacts':sorted(BASE),'trial_outcomes':[]} def inspect_case_artifacts(folder): checks=[] def check(name,value): checks.append({'check':name,'passed':bool(value)}) def flag(row,key,label): value=float(row[key]);check(label+key+':boolean',value in (0,1));return value==1 required=set(BASE) def get(name,needed=True): path=folder/(P+name+'.txt') if needed: required.add(path.name);return vector(path) check(name+':unavailable_file_absent',not path.exists());return None summary_rows=rows(folder/(P+'summary.csv')) summary={r['metric']:float(r['value']) for r in summary_rows} check('unique_summary',len(summary)==len(summary_rows)) meta=dict(line.split('=',1) for line in (folder/'metadata.txt').read_text().splitlines() if '=' in line) for key,metadata_key in {'outer_linear_tolerance':'linear_tol','seed_control':'coupled_seed_control', 'coupled_maximum_correction_ratio':'coupled_maximum_ratio','coupled_relative_tolerance':'coupled_tolerance', 'coupled_maximum_iterations':'coupled_iterations','coupled_restart':'coupled_restart', 'gravity_relative_tolerance':'gravity_tolerance','gravity_maximum_iterations':'gravity_iterations', 'gravity_restart':'gravity_restart','coupled_attempt_budget_per_independent_alpha':'coupled_attempts_per_iteration'}.items(): check('summary_metadata:'+key,summary[key]==float(meta[metadata_key])) check('strategy_scope',meta['strategy'] in ('coupled','curvature-coupled') and summary['gravity_path']==int(meta['strategy']=='curvature-coupled')) for key in ('outer_linear_tolerance','sufficient_decrease'): check('valid_control:'+key,math.isfinite(summary[key]) and 00) check('valid_gravity_relative_tolerance',math.isfinite(summary['gravity_relative_tolerance']) and summary['gravity_relative_tolerance']>=0) for key in ('coupled_maximum_iterations','coupled_restart','gravity_maximum_iterations','gravity_restart'): check('valid_control:'+key,math.isfinite(summary[key]) and summary[key]>0 and summary[key]==int(summary[key])) check('valid_correction_cap',math.isfinite(summary['coupled_maximum_correction_ratio']) and summary['coupled_maximum_correction_ratio']>0) check('fixed_driver_controls',summary['sufficient_decrease']==1.e-4 and summary['coupled_absolute_tolerance']==1.e-14 and summary['gravity_absolute_tolerance']==1.e-14) for name,value in {'schema_version':1,'independent_frozen_alphas':1,'accepted_commits':0, 'polynomial_direction_repairs':0,'sampled_enthalpy_policy':1,'coupled_attempt_budget_per_independent_alpha':1, 'probe_exception':0,'accepted_buffers_bitwise_identical':1,'normalization_factors_bitwise_identical':1, 'direction_buffers_bitwise_identical':1,'direction_buffers_unchanged_before_restoration':1, 'fresh_operator_and_inverse_current':1}.items():check(name,summary[name]==value) for i,name in enumerate(STATUS):check('status_mapping:'+name,summary['soc_status_'+name]==i) x,physical,f,p,j,sf,rf=[get(n) for n in ('base_normalized','base_physical','base_residual','direction','base_action','state_factors','residual_factors')] size=len(x) check('square_vector_dimensions',all(len(v)==size for v in (physical,f,p,j,sf,rf))) check('positive_normalization',all(v>0 for v in sf+rf)) check('base_physical_normalization',near_vector(x,[v*a for v,a in zip(physical,sf)],norm(x))) layout=rows(folder/(P+'layout.csv'));vl=layout_for(layout,'value',size);rl=layout_for(layout,'residual',size) gi=[i for name,(start,n) in vl.items() if name in GRAVITY_VALUES for i in range(start,start+n)] gravity_value_indices=set(gi) gr=[i for name,(start,n) in rl.items() if name in GRAVITY_ROWS for i in range(start,start+n)] f0,pn,jn=norm(f),norm(p),norm(j) check('finite_positive_base_residual',math.isfinite(f0) and f0>0) check('finite_direction_norm',math.isfinite(pn)) defect=[a+b for a,b in zip(f,j)];dn=norm(defect) check('base_defect_vector',near_vector(get('base_defect'),defect,f0+jn)) outer_threshold=summary['outer_linear_tolerance']*f0;outer_gate=dn<=outer_threshold for key,value in {'base_residual_norm':f0,'base_merit':.5*f0*f0,'direction_norm':pn, 'base_merit_slope':dot(f,j),'outer_verified_defect_norm':dn,'outer_verified_relative_defect':dn/f0, 'outer_verified_threshold':outer_threshold}.items():check(key,close(summary[key],value,f0+jn)) check('outer_gate',summary['outer_verified_gate']==int(outer_gate)) check('outer_failed_gate_skips_trials',summary['outer_gate_failure_skips_trials']==int(not outer_gate)) check('restored_residual_vector',near_vector(get('restored_residual'),f,f0)) check('restored_action_vector',near_vector(get('restored_action'),j,jn)) restored_f=norm(minus(get('restored_residual'),f));restored_j=norm(minus(get('restored_action'),j)) check('restoration_envelope',math.isfinite(summary['maximum_base_restoration_error']) and restored_f<=summary['maximum_base_restoration_error']+1024*EPS*f0 and summary['maximum_base_restoration_error']<=1.e-10*f0) check('restored_action_error',close(summary['restored_base_action_error'],restored_j,jn) and restored_j<=1.e-10*max(jn,1.e-28)) for name in ('gravity_path','seed_control'):flag(summary,name,'summary:') expected_alphas=[1.] if summary['seed_control']==1 else [1/16,1/64,1/256,1/1024] for i,a in enumerate(expected_alphas):check('alpha_'+str(i),summary['alpha_'+str(i)]==a) trial_rows=rows(folder/(P+'trials.csv'));block_rows=rows(folder/(P+'blocks.csv')) check('complete_trial_grid',[int(r['sample']) for r in trial_rows]==(list(range(len(expected_alphas))) if outer_gate else [])) block_map={(int(r['sample']),r['stage'],r['block']):r for r in block_rows} check('unique_block_rows',len(block_map)==len(block_rows));seen_blocks=set() partitions={'all':(0,size),**rl} outcomes=[] totals={'attempts':0,'kept':0,'fallbacks':0,'iterations':0,'preparations':0,'restorations':0, 'solve_seconds':0.,'verification_seconds':0.,'preparation_seconds':0.,'refresh_seconds':0.} def block_check(sample,a,stage,residual): for name,(start,n) in partitions.items(): key=(sample,stage,name);seen_blocks.add(key);r=block_map[key] check(f'{sample}:{stage}:{name}:layout',int(r['offset'])==start and int(r['size'])==n and float(r['alpha'])==a) check(f'{sample}:{stage}:{name}:norm',close(float(r['residual_norm']),norm(residual[start:start+n]),norm(residual))) for row in trial_rows: i=int(row['sample']);a=float(row['alpha']);label=str(i)+':' check(label+'alpha',a==expected_alphas[i]);suffix='_a'+str(i) flags={k:flag(row,k,label) for k in ('gravity_path','raw_prepared','gravity_attempted','gravity_reported_gate', 'gravity_prepared_gate','before_prepared','before_usable','before_accepted','soc_called','soc_attempted', 'aux_reported_gate','aux_verified_gate','aux_verification_applied','corrected_metric_observed', 'corrected_accepted','selected_prepared','selected_accepted','selected_is_original','rhs_saved', 'correction_saved','verified_vectors_saved')} check(label+'gravity_policy',int(flags['gravity_path'])==summary['gravity_path']) prediction=[v+a*w for v,w in zip(f,j)];raw_state=get('raw_state'+suffix) check(label+'linear_prediction',near_vector(get('prediction'+suffix),prediction,f0+a*jn)) check(label+'raw_displacement',near_vector(raw_state,[v+a*w for v,w in zip(x,p)],norm(x)+a*pn)) vectors={} for stage,available,normkey,meritkey in ( ('raw',flags['raw_prepared'],'raw_norm','raw_merit'), ('before',flags['before_prepared'],'before_norm','before_merit'), ('corrected',flags['corrected_metric_observed'],'corrected_norm','corrected_merit'), ('selected',flags['selected_prepared'],'selected_norm','selected_merit')): residual=get(stage+'_residual'+suffix,available) state=raw_state if stage=='raw' else get(stage+'_state'+suffix,available) if available: check(label+stage+':dimensions',len(residual)==len(state)==size) rn=norm(residual);check(label+stage+':norm',close(float(row[normkey]),rn,rn)) check(label+stage+':merit',close(float(row[meritkey]),.5*rn*rn,rn*rn)) vectors[stage]=(state,residual);block_check(i,a,stage,residual) if stage in ('corrected','selected'): pv=get(stage+'_physical'+suffix) check(label+stage+':physical',near_vector(state,[v*b for v,b in zip(pv,sf)],norm(state))) else: check(label+stage+':unavailable_metrics',math.isnan(float(row[normkey])) and math.isnan(float(row[meritkey]))) if stage in ('corrected','selected'):get(stage+'_physical'+suffix,False) check(label+'raw_rejection', (int(row['raw_rejection_reason'])==-1 and int(row['raw_rejection_stage'])==-1) if flags['raw_prepared'] else (int(row['raw_rejection_reason'])>=0 and int(row['raw_rejection_stage'])>=0)) check(label+'gravity_attempt',flags['gravity_attempted']==(flags['gravity_path'] and flags['raw_prepared'])) if not flags['gravity_path']: check(label+'vacuous_gravity_gates',flags['gravity_reported_gate'] and flags['gravity_prepared_gate']) check(label+'raw_baseline_preparation',flags['before_prepared']==flags['raw_prepared']) get('gravity_correction'+suffix,False) if flags['before_prepared']: check(label+'raw_baseline_state',vectors['before'][0]==raw_state) check(label+'raw_baseline_residual',vectors['before'][1]==vectors['raw'][1]) else: save_gravity=flags['gravity_attempted'] and flags['gravity_reported_gate'] gc=get('gravity_correction'+suffix,save_gravity) if save_gravity: check(label+'gravity_partition_present',bool(gi) and bool(gr)) check(label+'gravity_only_correction',len(gc)==size and all(v==0 for k,v in enumerate(gc) if k not in gravity_value_indices)) if flags['before_prepared']: check(label+'gravity_displacement',near_vector(vectors['before'][0],[v+w for v,w in zip(raw_state,gc)],norm(raw_state)+norm(gc))) target=norm([vectors['before'][1][k]-prediction[k] for k in gr]) check(label+'gravity_affine_target',close(float(row['gravity_target_defect']),target,f0+a*jn)) if flags['gravity_prepared_gate']: threshold=float(row['gravity_target_threshold']);disagreement=float(row['gravity_affine_disagreement']) check(label+'gravity_prepared_necessary_gate',math.isfinite(threshold) and threshold>=0 and target<=threshold+512*EPS*f0 and math.isfinite(disagreement) and 0<=disagreement<=threshold) usable=flags['before_prepared'] and flags['gravity_reported_gate'] and flags['gravity_prepared_gate'] check(label+'before_usable',flags['before_usable']==usable) armijo=(1-summary['sufficient_decrease']*a)*.5*f0*f0 before_accept=usable and float(row['before_merit'])<=armijo check(label+'before_armijo',flags['before_accepted']==before_accept) check(label+'soc_called',flags['soc_called']==usable) attempted=usable and not before_accept check(label+'one_independent_attempt',flags['soc_attempted']==attempted) status=int(row['soc_status']);check(label+'known_status',0<=statuscap else 0) check(label+'core_decision',int(row['aux_core_status'])==expected_core) core=int(row['aux_core_status']) if core in (0,1,2): check(label+'finite_core_complete_evidence',flags['rhs_saved'] and flags['correction_saved'] and flags['aux_verification_applied'] and flags['verified_vectors_saved']) if core!=0: check(label+'failed_core_falls_back',name=={1:'linear_failure',2:'norm_limit',3:'non_finite'}.get(core)) check(label+'failed_core_no_preparation',int(row['soc_preparations'])==0 and int(row['soc_restorations'])==0 and not flags['corrected_metric_observed']) else: check(label+'admissible_core_requires_gates',flags['aux_reported_gate'] and flags['aux_verified_gate'] and flags['verified_vectors_saved'] and float(row['correction_norm'])<=cap) if flags['corrected_metric_observed']: check(label+'corrected_requires_admissible_core',core==0) check(label+'corrected_displacement',near_vector(vectors['corrected'][0], [v+w for v,w in zip(vectors['before'][0],correction)],norm(vectors['before'][0])+norm(correction))) better=float(row['corrected_merit'])=0) parts=sum(float(row['soc_'+k]) for k in ('refresh_seconds','solve_seconds','verification_seconds','preparation_seconds')) check(label+'soc_time_scope',float(row['soc_seconds'])+1.e-6>=parts) check(label+'sample_time_scope',float(row['total_sample_seconds'])+1.e-6>=sum(float(row[k]) for k in ('raw_seconds','gravity_seconds','soc_seconds','capture_seconds'))) for key in ('preparations','restorations','solve_seconds','verification_seconds','preparation_seconds','refresh_seconds'): totals[key]+=float(row['soc_'+key]) totals['iterations']+=int(row['aux_iterations']) outcomes.append({'sample':i,'alpha':a,'transaction_status':name,'before_usable':usable,'before_armijo':before_accept, 'correction_attempted':attempted,'aux_verified_gate':flags['aux_verified_gate'],'corrected_metric_observed':flags['corrected_metric_observed'], 'selected_armijo':flags['selected_accepted'],'before_residual_norm':float(row['before_norm']), 'selected_residual_norm':float(row['selected_norm']),'full_merit_improved':name=='kept', 'correction_norm':float(row['correction_norm']),'correction_cap':float(row['correction_cap']), 'auxiliary_iterations':int(row['aux_iterations']),'auxiliary_verified_defect_norm':float(row['aux_verified_true_norm']), 'corrected_affine_target_defect_norm':norm(minus(vectors['corrected'][1],prediction)) if flags['corrected_metric_observed'] else None, 'correction_mechanism':correction_mechanism(x,p,a,prediction,vectors['before'][1],vectors['corrected'][0], vectors['corrected'][1],correction,rhs,action) if attempted and flags['corrected_metric_observed'] and correction is not None and rhs is not None and action is not None else None, 'auxiliary_seconds':{k:float(row['soc_'+k]) for k in ('refresh_seconds','solve_seconds','verification_seconds','preparation_seconds')}, 'residual_norms_by_block':{stage:{block:norm(value[1][start:start+n]) for block,(start,n) in rl.items()} for stage,value in vectors.items()}}) check('complete_block_coverage',seen_blocks==set(block_map)) if outer_gate: for key,value in totals.items():check('soc_total:'+key,close(summary['soc_'+key],value,value)) preparations=rows(folder/(P+'preparations.csv')) check('preparation_sequence',[int(r['sequence']) for r in preparations]==list(range(1,len(preparations)+1))) check('explicit_preparation_count',summary['explicit_probe_preparations']==len(preparations)) for r in preparations: check('preparation_time:'+r['sequence'],math.isfinite(float(r['seconds'])) and float(r['seconds'])>=0) if r['kind']!='raw':check('base_restoration_prepares:'+r['sequence'],r['prepared']=='1' and close(float(r['residual_norm']),f0,f0)) raw_preparations=[r for r in preparations if r['kind']=='raw'] check('raw_preparation_coverage',[int(r['sample']) for r in raw_preparations]==[int(r['sample']) for r in trial_rows]) for prep,trial in zip(raw_preparations,trial_rows): label=prep['sample']+':raw_preparation:' for key in ('prepared','rejection_reason','rejection_stage'): check(label+key,prep[key]==trial['raw_'+key]) check(label+'alpha',float(prep['alpha'])==float(trial['alpha'])) measured=float(prep['residual_norm']);reported=float(trial['raw_norm']) check(label+'residual',close(measured,reported) if prep['prepared']=='1' else math.isnan(measured) and math.isnan(reported)) check(label+'time_scope',float(prep['seconds'])<=float(trial['raw_seconds'])+1.e-6) check('preparation_time_total',close(summary['explicit_probe_preparation_seconds'],sum(float(r['seconds']) for r in preparations))) check('final_restoration_present',bool(preparations) and preparations[-1]['kind']=='final_base_restore') check('total_time',math.isfinite(summary['total_seconds']) and summary['total_seconds']>=0) physical_observations=inspect_physical(folder,trial_rows,meta,check) check('no_unexplained_vector_artifacts',{p.name for p in folder.glob(P+'*.txt')}== {name for name in required if name.startswith(P) and name.endswith('.txt')}) passed=all(r['passed'] for r in checks) return sanitize({'passed':passed,'checks':checks,'observations_valid':passed,'metadata':meta,'summary':summary, 'required_artifacts':sorted(required),'outer_linear_gate':outer_gate,'trial_outcomes':outcomes if passed else [], 'physical_observations':physical_observations if passed else [], 'scope':'Independent saved-vector arithmetic and transaction/provenance audit. Frozen alpha samples are not a nonlinear trajectory. Physical replay metrics are checked for internal consistency and reported separately; no independent FEM quadrature is performed.', 'gravity_scope':'Saved vectors independently check the affine gravity target defect. This schema does not independently reconstruct the gravity inner solve or its contribution-based arithmetic allowance.', 'operator_scope':'Saved SOC J actions verify the reported algebraic defect; this checker does not perform another FEM action.'}) def inspect_physical(folder,trials,metadata,check): """Check replay binding and scalar identities without certifying the star.""" records=rows(folder/(P+'physical.csv')) measured={(int(r['sample']),r['source'],int(r['quadrature']),r['metric']):float(r['value']) for r in records} check('physical:unique_measurements',len(measured)==len(records)) used=set() def get(sample,source,q,key): index=(sample,source,q,key);used.add(index);return measured[index] for key in ('accepted_buffers_unchanged_before_restoration','accepted_buffers_unchanged_after_restoration', 'normalization_factors_unchanged','direction_buffers_unchanged','accepted_operator_and_inverse_current'): check('physical:'+key,get(-1,'restoration',0,key)==1) selected=[r for r in trials if r['selected_prepared']=='1'] check('physical:snapshot_count',get(-1,'replay',0,'selected_snapshots_measured')==len(selected)) check('physical:sample_coverage',{k[0] for k in measured if k[0]>=0}=={int(r['sample']) for r in selected}) observations=[] for trial in selected: sample=int(trial['sample']);source=P+'selected_physical_a'+str(sample)+'.txt' saved=vector(folder/source);label='physical:'+str(sample)+':' check(label+'fresh_preparation',get(sample,source,0,'fresh_preparation_passed')==1) for q in (14,18): prefix=label+str(q)+':' m={key[3]:value for key,value in measured.items() if key[:3]==(sample,source,q)} used.update(key for key in measured if key[:3]==(sample,source,q)) check(prefix+'quadrature',m['quadrature_order']==q) check(prefix+'physics',m['polytropic_index']==float(metadata['n']) and m['polytropic_constant']==float(metadata['K']) and m['target_mass']==float(metadata['M'])) for key in ('stellar_samples','valid_stellar_samples','invalid_stellar_samples','negative_density_samples', 'negative_enthalpy_samples','invalid_density_eos_samples','invalid_enthalpy_eos_samples'): check(prefix+key,math.isfinite(m[key]) and m[key]>=0 and m[key]==int(m[key])) check(prefix+'sample_accounting',m['valid_stellar_samples']+m['invalid_stellar_samples']==m['stellar_samples']) complete=m['invalid_stellar_samples']==0 and m['valid_stellar_samples']>0 and math.isfinite(m['volume']) and m['volume']>0 check(prefix+'complete_measurement',m['complete_volume_measurement']==int(complete)) hp=complete and m['invalid_enthalpy_eos_samples']==0 rp=complete and m['negative_density_samples']==0 and m['invalid_density_eos_samples']==0 check(prefix+'enthalpy_pressure_definition',m['pressure_from_enthalpy_defined_everywhere']==int(hp)) check(prefix+'density_pressure_definition',m['pressure_from_density_defined_everywhere']==int(rp)) check(prefix+'snapshot_roundtrip',0<=m['saved_prepared_l2_difference']<=1024*EPS*(1+norm(saved)) and 0<=m['saved_prepared_maximum_difference']<=m['saved_prepared_l2_difference']+1024*EPS and 0<=m['saved_prepared_maximum_scaled_difference']<=1024*EPS) selected_norm=float(trial['selected_norm']) # Repreparing a denormalized snapshot introduces roundoff. This # tolerance checks replay identity, not physical residual accuracy. check(prefix+'selected_residual_binding',math.isfinite(m['normalized_bordered_residual']) and abs(m['normalized_bordered_residual']-selected_norm)<=1024*EPS*(1+selected_norm)) check(prefix+'central_border_finite',math.isfinite(m['central_border']) and math.isfinite(m['normalized_border_action'])) if metadata['central_phase']=='0': check(prefix+'unbordered_model',m['central_border']==0 and m['normalized_border_action']==0 and close(m['normalized_unbordered_residual'],m['normalized_bordered_residual'],selected_norm)) if complete: check(prefix+'mass_error',close(m['mass_absolute_error'],abs(m['mass']-m['target_mass']),abs(m['mass'])+m['target_mass'])) check(prefix+'sampled_signs', (m['minimum_density']<0)==(m['negative_density_samples']>0) and (m['minimum_enthalpy']<0)==(m['negative_enthalpy_samples']>0)) w=m['binding_energy'];wf=m['force_binding_energy'];t=m['kinetic_energy'] virials={} for prefix_pressure,defined,pressure_key in (('',rp,'pressure_integral'),('enthalpy_',hp,'enthalpy_pressure_integral')): pressure=m[pressure_key] check(prefix+pressure_key+':definition',math.isfinite(pressure) if defined else math.isnan(pressure)) for force,binding in (('',w),('force_',wf)): key=prefix_pressure+force+'virial_';signed=m[key+'signed'];error=m[key+'error'] valid=defined and math.isfinite(w) and w<0 if valid: expected=(2*t+binding+3*pressure)/abs(w) scale=(2*abs(t)+abs(binding)+3*abs(pressure))/abs(w) check(prefix+key+'identity',close(signed,expected,scale) and close(error,abs(signed),scale)) else:check(prefix+key+'undefined',math.isnan(signed) and math.isnan(error)) virials[key+'within_1e5']=(error<=1.e-5) if valid and math.isfinite(error) else None observations.append({'sample':sample,'quadrature':q,'selected_armijo':trial['selected_accepted']=='1', 'complete_volume_measurement':complete,'pressure_from_density_defined':rp,'pressure_from_enthalpy_defined':hp, 'negative_density_samples':m['negative_density_samples'],'minimum_density':m['minimum_density'], 'negative_enthalpy_samples':m['negative_enthalpy_samples'],'minimum_enthalpy':m['minimum_enthalpy'], 'global_enthalpy_certificate':'not_measured','virial_status':virials,'reported_metrics':m}) check('physical:source_and_quadrature_coverage',used==set(measured)) return observations def suppress_observations(results): for result in results.values(): result.update(passed=False,observations_valid=False,trial_outcomes=[],physical_observations=[]) def inspect_campaign(campaign): campaign=Path(campaign).resolve();checks=[];results={} def check(name,value):checks.append({'check':name,'passed':bool(value)}) try: provenance=json.loads((campaign/'provenance.json').read_text());cases=provenance['cases'] check('executable_hash',digest(campaign/'plateau_diagnostic')==provenance['binary_sha256']) check('mesh_hash',digest(Path(provenance['mesh']))==provenance['mesh_sha256']) check('unique_cases',bool(cases) and len(cases)==len({c['id'] for c in cases})) check('manifest_snapshot',json.loads((campaign/'cases.json').read_text())==cases) for case in cases: name=case['id'];folder=campaign/name;args=case['arguments'];record=json.loads((campaign/(name+'_run.json')).read_text()) check(name+':completed',bool(record.get('finished_utc')) and record.get('exit_code')==0 and not record.get('timed_out',False)) check(name+':command',record.get('id')==name and record.get('command')==[str(campaign/'plateau_diagnostic'),*args,'--mesh',provenance['mesh'],'--output',str(folder)]) expected={} for option,saved in (('--probe-state','base_physical'),('--probe-direction','direction')): if option not in args:raise ValueError('A frozen coupled trial must bind '+option) path=Path(args[args.index(option)+1]);path=path if path.is_absolute() else Path(provenance['source'])/path expected[str(path.resolve())]=saved check(name+':required_input_hashes',expected.keys()<=record.get('input_state_hashes',{}).keys()) for path,value in record.get('input_state_hashes',{}).items():check(name+':input:'+path,digest(Path(path))==value) for filename,value in record.get('output_hashes',{}).items():check(name+':output:'+filename,digest(folder/filename)==value) for path,saved in expected.items(): source=vector(Path(path));target=vector(folder/(P+saved+'.txt')) check(name+':input_binding:'+saved,source==target if saved=='direction' else near_vector(source,target,norm(source))) result=inspect_case(folder);results[name]=result;meta=result.get('metadata',{}) check(name+':required_output_hashes',set(result['required_artifacts'])<=record.get('output_hashes',{}).keys()) check(name+':expert_probe','--coupled-trial-probe' in args and meta.get('coupled_trial_probe')=='1') bindings={'--index':'n','--angular-momentum':'J','--linear-tol':'linear_tol','--coupled-tol':'coupled_tolerance', '--coupled-iterations':'coupled_iterations','--coupled-restart':'coupled_restart','--coupled-max-ratio':'coupled_maximum_ratio', '--coupled-attempts':'coupled_attempts_per_iteration','--gravity-tol':'gravity_tolerance', '--gravity-iterations':'gravity_iterations','--gravity-restart':'gravity_restart'} for option,key in bindings.items(): if option in args:check(name+':requested_'+key,key in meta and float(meta[key])==float(args[args.index(option)+1])) if '--strategy' in args:check(name+':strategy',meta.get('strategy')==args[args.index('--strategy')+1]) if '--no-central' in args:check(name+':no_central',meta.get('central_phase')=='0') check(name+':independent_alpha_metadata',meta.get('coupled_trial_alphas_are_independent')=='1') check(name+':sampled_enthalpy_scope',meta.get('coupled_trial_admissibility')=='sampled_enthalpy_without_repair') check(name+':seed_control',meta.get('coupled_seed_control')==str(int('--coupled-seed-control' in args))) passed=all(c['passed'] for c in checks) and all(r['passed'] for r in results.values()) if not all(c['passed'] for c in checks): suppress_observations(results) return {'passed':passed,'source_commit':provenance['commit'],'checker_sha256':digest(Path(__file__)), 'checker_utility_sha256':digest(Path(__file__).with_name('check_nonlinear_step.py')), 'provenance_sha256':digest(campaign/'provenance.json'),'provenance_checks':checks,'cases':results, 'scope':'Artifact validity and numerical/physical success are separate conclusions.'} except (OSError,ValueError,KeyError,TypeError,IndexError) as error: suppress_observations(results) return {'passed':False,'provenance_checks':checks+[{'check':'complete_readable_campaign','passed':False,'error':str(error)}],'cases':results} def main(): parser=argparse.ArgumentParser(description=__doc__) parser.add_argument('campaign',type=Path);parser.add_argument('--output',type=Path,required=True) args=parser.parse_args() if args.output.resolve()==args.campaign.resolve() or args.campaign.resolve() in args.output.resolve().parents: parser.error('Write the review outside the immutable campaign.') result=inspect_campaign(args.campaign);args.output.write_text(json.dumps(result,indent=2,allow_nan=False)+'\n') print('PASS' if result['passed'] else 'FAIL',args.output) for name,case in result['cases'].items():print(name,'artifacts=',case['passed'],'outer_linear=',case.get('outer_linear_gate')) return 0 if result['passed'] else 1 if __name__=='__main__':raise SystemExit(main())