from pathlib import Path
import csv,datetime,json,math,sys
sys.path.insert(0,r'D:/Photometer_research_20261022/research/cloud_transport/private_readers')
from scipy.optimize import brentq

p=Path(r'D:/Photometer_research_20261022/revision_20261022_r4/analysis/analytic/deadtime')
p.mkdir(exist_ok=True)
tau=17e-9;rows=[]
for background in [50.,1e6,1e8]:
 for peak_signal in [1e6,5e6,5e7,1e8,1e9,3e9,1e10]:
  x=peak_signal*tau/(1+background*tau)
  frac10=.1/(1+.9*x);frac90=.9/(1+.1*x)
  shrink=(frac90-frac10)/.8
  def counter(rate):return rate/(1+rate*tau)
  mb=counter(background);mp=counter(background+peak_signal)
  def normalized_response(f):return (counter(background+peak_signal*f)-mb)/(mp-mb)
  numeric10=brentq(lambda f:normalized_response(f)-.1,0,1)
  numeric90=brentq(lambda f:normalized_response(f)-.9,0,1)
  assert abs(numeric10-frac10)<1e-10 and abs(numeric90-frac90)<1e-10
  for input_rise in [30.,80.,200.,300.]:
   rows.append(dict(assumed_nonparalyzable_deadtime_s=tau,background_true_cps=background,
    peak_true_signal_cps=peak_signal,peak_total_true_cps=background+peak_signal,
    peak_mean_registered_cps=mp,baseline_mean_registered_cps=mb,
    peak_total_fraction_lost=1-mp/(background+peak_signal),effective_signal_occupancy=x,
    input_linear_10_90_rise_us=input_rise,
    output_uncorrected_10_90_rise_us=input_rise*shrink,
    output_over_input_rise=shrink,source_fraction_at_output10pct=frac10,source_fraction_at_output90pct=frac90,
    inverse_mean_rate_slope_at_peak=(1+(background+peak_signal)*tau)**2,
    approximation='Quasi-staticmeanresponseofnonparalyzablecounter; noeventwaveformormisclassificationprobability'))
with (p/'conditional_rise_bias.csv').open('w',newline='',encoding='utf-8') as f:
 w=csv.DictWriter(f,fieldnames=list(rows[0]));w.writeheader();w.writerows(rows)
report={'recorded_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),
 'status':'OWN_CONDITIONAL_MEAN_RESPONSE_DIAGNOSTIC_NOT_MEASURED_FLIGHT_BIAS',
 'questions':['A2','C7','C8','C9','E16','G21','G22'],
 'primary_context':'Chanrionetal2019MMIA DOI10.1007/s11214-019-0593-y section6.3 photon-countconversion;17ns337deadtimeisafrozenstudyassumption/actualCDFparameter. Exactreceiverarchitectureanddeadtime mustbeverified.',
 'own_derivation':{
  'true_rate':'r(t)=b+S*f(t), flinear0to1overrampT',
  'registered_mean_rate':'m(r)=r/(1+r*tau), strictlyNONPARALYZABLEmodel',
  'baseline_subtracted_fraction_of_registered_peak':'y=f*(1+x)/(1+x*f), x=S*tau/(1+b*tau)',
  'inverse_fraction':'f(y)=y/[1+x*(1-y)]',
  '10_90_ratio':'[.9/(1+.1*x)-.1/(1+.9*x)]/.8',
  '10_90_ratio_simplified':'(1+x)/(1+x+.09*x*x); symmetric10–90compressionbeginsatsecondorderforsmallx',
  'generic_intrinsic_waveform':'t_y=f_inverse(y/[1+x*(1-y)]); simplifiedcompressionaboveisspecifictolinearramp',
  'mean_correction':'r=m/(1-m*tau), dr/dm=1/(1-m*tau)^2',
  'approximation':'rchangeson30–300us scales muchlongerthan17ns; quasistaticexpectationonly'},
 'scope_limits':['OnlyRAWuncorrectedmeanwaveformshape; publishedcorrectedMMIAwaveformscannotbeaccusedofthisbiasfromthisdiagnostic','Noactualsourcebrightnessdistributionordetectorselected','Noafterpulsing,paralyzableholdoff,discriminatorwalk,gainsag,baselineestimation,electronics/samplingorcountlikelihood','StationaryPoissonarrivalmeanresponseappliedquasi-statically; notexactstochasticwaveform','Mathematicalinversionrecoversthemeanonlyforavalidknownmodel; doesnotrestorelostphotons orPoissonstatistics','Binwiseinverseisnonlinear: expectedinverseofmeasuredcountisnotinverseofexpectedcount','A200usinputcangiveraw~tens-usapparentrisewhenstronglysaturated; thisisnot evidencefainteventsactuallydoso','Simplyenlargingapertureatfixedthroughputraisesmeanrate; gainchangesaloneare notassumedtocorrectdeadtime'],
 'validation':{'analyticversusindependentnumericalamplituderoots':21,'rows':len(rows)},
 'examples':[r for r in rows if r['background_true_cps']==50 and r['input_linear_10_90_rise_us']==200]}
(p/'derivation_and_qualification.json').write_text(json.dumps(report,indent=2),encoding='utf-8')
print(json.dumps({'rows':len(rows),'examples':report['examples']},indent=2))
