# Data-generating model shared by make_data.py and simulation.py. Python 3.13, NumPy.
# Saturation binding: total and nonspecific binding (fmol/mg protein) at 8 radioligand concentrations, triplicate wells,
# control and treated membranes prepared side by side in each independent experiment.
import numpy as np
L = np.array([0.1, 0.2, 0.4, 0.8, 1.6, 3.2, 6.4, 10.0])   # nM; true Kd 1 nM, so 0.1 to 10 x Kd
WELLS = 3

def experiment(rng, bmax_ratio=0.7, kd_ratio=1.0, kd=1.0, bmax=500.0, sd_day=0.10, sd_prep_kd=0.08, sd_prep_bmax=0.10, sd_well=0.06):
    """One experiment: control and treated membranes. Treated Bmax = bmax_ratio x control, Kd = kd_ratio x control.
    Nonspecific binding rises linearly with ligand. Noise is multiplicative (about 6% per well)."""
    day_kd = rng.normal(0, sd_day); rows = []
    nsb_slope = 12.0 * np.exp(rng.normal(0, 0.1))               # fmol/mg per nM
    for cond, br, kr in (("control", 1.0, 1.0), ("treated", bmax_ratio, kd_ratio)):
        k = kd * kr * np.exp(day_kd + rng.normal(0, sd_prep_kd)); b = bmax * br * np.exp(rng.normal(0, sd_prep_bmax))
        for l in L:
            spec = b * l / (k + l); nsb = nsb_slope * l
            for w in range(WELLS):
                rows.append((cond, l, w + 1, (spec + nsb) * np.exp(rng.normal(0, sd_well)), nsb * np.exp(rng.normal(0, sd_well))))
    return rows
