import%20marimo%0A%0A__generated_with%20%3D%20%220.19.2%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Biophysical%20model%20for%20how%20antibody%20affinity%20shapes%20the%20impact%20of%20viral%20antigen%20mutations%20on%20antibody%20IgG%20versus%20Fab%20neutralization%0A%0A%20%20%20%20%23%23%20Overview%0A%20%20%20%20Here%20we%20describe%20a%20biophysical%20model%20of%20how%20mutations%20to%20a%20viral%20antigen%20impact%20neutralization%20by%20IgG%20versus%20Fab%20depends%20on%20the%20antibody%20affinity%20for%20the%20antigen.%0A%20%20%20%20The%20two%20key%20effects%20modeled%20here%20are%20the%20bivalent%20binding%20of%20the%20IgG%20(but%20not%20Fab)%20and%20the%20ligand%20depletion%20(titration)%20that%20can%20put%20a%20floor%20on%20the%20lowest%20measurable%20neutralization%20value.%0A%0A%20%20%20%20We%20first%20describe%20the%20quantitative%20model%2C%20then%20make%20an%20interactive%20plot%20demonstrating%20the%20behavior%20of%20the%20model%2C%20and%20finally%20make%20a%20static%20plot%20comparing%20the%20model%20to%20real%20data%20generated%20for%20RSV%20F%20pseudovirus%20neutralization%20by%20the%20nirsevimab%20IgG%20or%20Fab.%0A%0A%20%20%20%20This%20document%20is%20part%20of%20a%20study%20by%20Cassie%20Simonich%2C%20Teagan%20McMahon%2C%20and%20%5BJesse%20Bloom%5D(https%3A%2F%2Fjbloomlab.org%2F)%20on%20how%20mutations%20to%20RSV%20F%20affect%20its%20antibody%20neutralization.%0A%20%20%20%20See%20%5Bhttps%3A%2F%2Fgithub.com%2Fjbloomlab%2FIgG-vs-Fab-neutralization%5D(https%3A%2F%2Fgithub.com%2Fjbloomlab%2FIgG-vs-Fab-neutralization)%20for%20the%20source%20code%20for%20this%20analysis.%0A%0A%20%20%20%20%23%23%20Biophysical%20model%0A%0A%20%20%20%20%23%23%23%20Simple%20schematic%20illustrating%20principle%20of%20model%0A%20%20%20%20The%20schematic%20below%20illustrates%20the%20principle%20of%20the%20model%20(this%20schematic%20was%20generated%20with%20BioRender)%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.image(%22biorender%2FIgG-vs-Fab%20neutralization.png%22%2C%20width%3D%22100%25%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Fab%20binding%0A%20%20%20%20Let%20the%20binding%20affinity%20of%20the%20Fab%20have%20dissociation%20constant%20%24K_D%24.%0A%20%20%20%20There%20are%20two%20possible%20states%2C%20unbound%20(%24U%24)%20and%20bound%20(%24B%24).%0A%20%20%20%20The%20Boltzmann%20weights%20for%20these%20two%20states%20as%20a%20function%20of%20the%20molar%20concentration%20%24c%24%20of%20Fab%20are%20%24w_U%5Cleft(c%5Cright)%20%3D%201%24%20and%20%24w_B%5Cleft(c%5Cright)%20%3D%20%5Cfrac%7Bc%7D%7BK_D%7D%24.%0A%0A%20%20%20%20So%20the%20probability%20of%20the%20unbound%20state%20as%20a%20function%20of%20%24c%24%20is%0A%20%20%20%20%24p%5E%7B%5Crm%7BFab%7D%7D_u%5Cleft(c%5Cright)%20%3D%20%5Cfrac%7Bw_U%5Cleft(c%5Cright)%7D%7Bw_U%5Cleft(c%5Cright)%20%2B%20w_B%5Cleft(c%5Cright)%7D%20%3D%20%5Cfrac%7B1%7D%7B1%20%2B%20c%2FK_D%7D.%24%0A%0A%20%20%20%20This%20two-state%20partition-function%20formulation%20for%20monovalent%20binding%20follows%20classic%20multivalency%20treatments%20(%5BPerelson%20%26%20DeLisi%2C%201980%5D(https%3A%2F%2Fwww.sciencedirect.com%2Fscience%2Farticle%2Fabs%2Fpii%2F0025556480900176)%3B%20%5BHlavacek%20et%20al.%2C%201999%5D(https%3A%2F%2Fpubmed.ncbi.nlm.nih.gov%2F10354429%2F)).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20IgG%20binding%0A%20%20%20%20For%20the%20IgG%2C%20we%20will%20assume%20each%20arm%20of%20the%20bivalent%20IgG%20has%20the%20same%20monovalent%20affinity%20for%20the%20viral%20antigen%20as%20the%20Fab%20described%20above.%0A%20%20%20%20However%2C%20once%20the%20first%20arm%20binds%2C%20the%20effective%20concentration%20%24c_%7B%5Crm%7Beff%7D%7D%24%20of%20the%20second%20arm%20is%20set%20by%20the%20density%2Fspacing%20of%20viral%20antigens%20and%20the%20reach%20of%20the%20antibody.%0A%20%20%20%20Here%20we%20do%20not%20consider%20those%20factors%20(antigen%20density%2C%20antibody%20reach%2C%20etc)%20separately%2C%20but%20just%20collapse%20them%20into%20the%20single%20effective%20concentration%20of%20the%20second%20arm%20after%20the%20first%20has%20bound%2C%20%24c_%7B%5Crm%7Beff%7D%7D%24.%0A%20%20%20%20The%20definition%20of%20an%20effective%20local%20concentration%20for%20the%20second%20arm%2C%20set%20by%20geometry%2Flinker%20statistics%20rather%20than%20bulk%20concentration%20mirrors%20prior%20work%20(%5BEinav%20et%20al.%2C%202019%5D(https%3A%2F%2Fwww.cell.com%2Fcell-systems%2Fpdf%2FS2405-4712%252819%252930315-1.pdf)%3B%20%5BMammen%2C%20Choi%2C%20%26%20Whitesides%2C%201998%5D(https%3A%2F%2Fpubmed.ncbi.nlm.nih.gov%2F29711117%2F)%3B%20%5BFasting%20et%20al.%2C%202012%5D(https%3A%2F%2Fonlinelibrary.wiley.com%2Fdoi%2Ffull%2F10.1002%2Fanie.201201114)).%0A%20%20%20%20In%20realistic%20scenarios%2C%20if%20bivalent%20binding%20is%20possible%20then%20%24c_%7B%5Crm%7Beff%7D%7D%20%5Cgg%20c%24%20since%20the%20effective%20concentration%20of%20the%20second%20Fab%20arm%20in%20proximity%20to%20the%20viral%20antigen%20after%20the%20first%20arm%20has%20bound%20is%20much%20higher%20than%20the%20overall%20IgG%20concentration%20(note%20that%20concentrations%20should%20be%20in%20*molar*%20units%20rather%20than%20ng%2Fml%20in%20order%20to%20allow%20the%20same%20concentration%20scale%20to%20be%20used%20for%20Fab%20and%20IgG%2C%20since%20they%20have%20different%20molecular%20weights).%0A%0A%20%20%20%20There%20are%20now%20three%20possible%20states%2C%20unbound%20(%24U%24)%2C%20a%20single%20Fab%20arm%20bound%20(%24B_1%24)%2C%20and%20both%20Fab%20arms%20bound%20(%24B_2%24).%0A%20%20%20%20The%20Boltzmann%20weights%20for%20these%20three%20states%20as%20a%20function%20of%20the%20molecular%20concentration%20of%20IgG%20are%20%24w_U%5Cleft(c%5Cright)%20%3D%201%24%2C%20%24w_%7BB_1%7D%5Cleft(c%5Cright)%20%3D%20%5Cfrac%7Bc%7D%7BK_D%7D%24%2C%20and%20%24w_%7BB_2%7D%5Cleft(c%5Cright)%20%3D%20%5Cfrac%7Bc%7D%7BK_D%7D%5Ctimes%5Cfrac%7B%20c_%7B%5Crm%7Beff%7D%7D%7D%7BK_D%7D%24.%0A%20%20%20%20Further%2C%20note%20that%20state%20%24B_1%24%20has%20a%20multiplicity%20of%20two%20since%20either%20of%20the%20Fab%20arms%20can%20bind%20(%5BPerelson%20%26%20DeLisi%2C%201980%5D(https%3A%2F%2Fwww.sciencedirect.com%2Fscience%2Farticle%2Fabs%2Fpii%2F0025556480900176)).%0A%0A%20%20%20%20So%20the%20probability%20of%20the%20unbound%20state%20as%20a%20function%20of%20%24c%24%20is%20(accounting%20for%20the%20multiplicity%20of%20two%20for%20%24B_1%24)%0A%20%20%20%20%24p_u%5E%7B%5Crm%7BIgG%7D%7D%5Cleft(c%5Cright)%20%3D%20%5Cfrac%7Bw_U%5Cleft(c%5Cright)%7D%7Bw_U%5Cleft(c%5Cright)%20%2B%202%20w_%7BB_1%7D%5Cleft(c%5Cright)%20%2B%20w_%7BB_2%7D%5Cleft(c%5Cright)%7D%20%3D%20%5Cfrac%7B1%7D%7B1%20%2B%202%20%5Cfrac%7Bc%7D%7BK_D%7D%20%2B%20%5Cfrac%7Bc%7D%7BK_D%7D%20%5Cfrac%7Bc_%7B%5Crm%7Beff%7D%7D%7D%7BK_D%7D%7D%20%3D%20%5Cfrac%7B1%7D%7B1%20%2B%20%5Cfrac%7Bc%7D%7BK_D%7D%5Cleft(2%20%2B%20%5Cfrac%7Bc_%7B%5Crm%7Beff%7D%7D%7D%7BK_D%7D%5Cright)%7D.%24%0A%0A%20%20%20%20Note%20that%20the%20effective%20dissociation%20constant%20of%20the%20IgG%20is%20%24K_D%5E%7B%5Crm%7BIgG%7D%7D%20%3D%20K_D%20%2F%20%5Cleft(2%20%2B%20%5Cfrac%7Bc_%7B%5Crm%7Beff%7D%7D%7D%7BK_D%7D%5Cright)%24.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Assumption%3A%20binding%20affinity%20equals%20neutralization%20potency%0A%20%20%20%20In%20what%20follows%2C%20we%20assume%20that%20binding%20affinity%20equals%20neutralization%20potency%2C%20such%20that%20the%20neutralization%20IC50%20of%20the%20Fab%20is%20equivalent%20to%20%24K_D%24.%0A%20%20%20%20In%20other%20words%2C%20we%20equate%20fraction%20infectivity%20with%20fraction%20of%20viral%20epitopes%20(%24p_u%24)%3B%20as%20described%20immediately%20below%2C%20deviations%20can%20occur%20under%20multi-hit%20models.%0A%0A%20%20%20%20Specifically%2C%20how%20fraction%20bound%20(binding%20affinity)%20relates%20to%20neutralization%20potency%20depends%20on%20how%20many%20antibodies%20must%20bind%20to%20a%20virion%20in%20order%20to%20neutralize%20it.%0A%20%20%20%20The%20quantitative%20relationship%20between%20binding%20affinity%20and%20neutralization%20potency%20as%20well%20as%20its%20impact%20on%20the%20neutralization%20curve%20slope%20can%20be%20modeled%20under%20assumptions%20about%20the%20number%20of%20spikes%20per%20virions%20and%20the%20number%20that%20must%20be%20bound%20to%20neutralize%20(see%20%5BMagnus%20et%20al%2C%202013%5D(https%3A%2F%2Fjournals.plos.org%2Fploscompbiol%2Farticle%3Fid%3D10.1371%252Fjournal.pcbi.1002900)%2C%20%5BBrandenburg%20et%20al%2C%202017%5D(https%3A%2F%2Fjournals.plos.org%2Fplospathogens%2Farticle%3Fid%3D10.1371%252Fjournal.ppat.1006313)%2C%20%5BPierson%20et%20al.%2C%202007%5D(https%3A%2F%2Fpmc.ncbi.nlm.nih.gov%2Farticles%2FPMC2656919%2F)%3B%20%5BKlasse%2C%202014%5D(https%3A%2F%2Fpubmed.ncbi.nlm.nih.gov%2F27099867%2F)%2C%20%5BYang%20et%20al%2C%202005%5D(https%3A%2F%2Fpmc.ncbi.nlm.nih.gov%2Farticles%2FPMC1075697)).%0A%0A%20%20%20%20Here%20we%20are%20going%20to%20**ignore**%20this%20issue%20for%20now%2C%20and%20just%20conflate%20neutralization%20IC50%20with%20Fab%20%24K_D%24.%0A%20%20%20%20This%20is%20a%20quantitative%20simplification%2C%20but%20should%20not%20affect%20the%20main%20qualitative%20trends--and%20has%20the%20advantage%20of%20not%20requiring%20us%20to%20estimate%20difficult%20to%20determine%20quantities%20such%20as%20how%20many%20spikes%20there%20are%20per%20virion%20and%20what%20fraction%20of%20them%20must%20be%20bound%20for%20neutralization.%0A%20%20%20%20Note%20that%20in%20the%20limiting%20case%20where%20this%20is%20just%20one%20spike%20per%20virion%20and%20binding%20one%20site%20on%20it%20is%20sufficient%20for%20neutralization%2C%20then%20ignoring%20this%20issue%20has%20no%20effect.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Estimating%20%24c_%7B%5Crm%7Beff%7D%7D%24%20from%20the%20IgG%20versus%20Fab%20IC50%0A%20%20%20%20We%20can%20estimate%20%24c_%7B%5Crm%7Beff%7D%7D%24%20(the%20parameter%20that%20describes%20the%20extent%20of%20avidity%20from%20bivalent%20binding)%20from%20the%20IC50%20(or%20midpoint%20on%20the%20neutralization%20curve)%20of%20the%20IgG%20versus%20Fab.%0A%20%20%20%20In%20particular%2C%20let%20%24m_%7B%5Crm%7BFab%7D%7D%24%20and%20%24m_%7B%5Crm%7BIgG%7D%7D%24%20be%20the%20molar%20concentrations%20at%20the%20midpoint%20(IC50)%20of%20the%20Fab%20versus%20IgG%20neutralization%20curves%2C%20respectively.%0A%20%20%20%20Then%20%24K_D%20%3D%20m_%7B%5Crm%7BFab%7D%7D%24%20and%20%241%2F2%20%3D%20%5Cfrac%7B1%7D%7B1%20%2B%20%5Cfrac%7Bm_%7B%5Crm%7BIgG%7D%7D%7D%7Bm_%7B%5Crm%7BFab%7D%7D%7D%5Cleft(2%20%2B%20%5Cfrac%7Bc_%7B%5Crm%7Beff%7D%7D%7D%7Bm_%7B%5Crm%7BFab%7D%7D%7D%5Cright)%7D.%24%0A%20%20%20%20Solving%20for%20%24c_%7B%5Crm%7Beff%7D%7D%24%20yields%0A%20%20%20%20%24c_%7B%5Crm%7Beff%7D%7D%20%3D%20m_%7B%5Crm%7BFab%7D%7D%5Cleft(%5Cfrac%7Bm_%7B%5Crm%7BFab%7D%7D%7D%7Bm_%7B%5Crm%7BIgG%7D%7D%7D%20-%202%5Cright).%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Impact%20of%20mutation(s)%0A%20%20%20%20Consider%20one%20or%20more%20mutations%20that%20cause%20a%20fold%20change%20in%20Fab%20IC50%20of%20%24f_%7B%5Crm%7Bmut%7D%7D%24.%0A%20%20%20%20For%20instance%2C%20a%20mutation%20that%20increases%20the%20Fab%20IC50%20by%2020-fold%20would%20have%20%24f_%7B%5Crm%7Bmut%7D%7D%20%3D%2020%24.%0A%20%20%20%20Note%20that%20mutations%20that%20cause%20a%20fixed%20change%20in%20the%20free%20energy%20of%20binding%20are%20expected%20to%20cause%20the%20same%20fold%20change%20in%20Fab%20IC50%20in%20any%20genetic%20background.%0A%0A%20%20%20%20In%20the%20presence%20of%20these%20mutation(s)%2C%20the%20fraction%20of%20Fab%20not%20neutralized%20becomes%0A%20%20%20%20%24p_u%5E%7B%5Crm%7BFab%7D%7D%5Cleft(c%5Cright)%20%3D%20%5Cfrac%7B1%7D%7B1%20%2B%20%5Cfrac%7Bc%7D%7Bf_%7B%5Crm%7Bmut%7D%7D%20%5Ctimes%20K_D%7D%7D%24%0A%20%20%20%20and%20the%20fraction%20of%20IgG%20not%20neutralized%20becomes%0A%20%20%20%20%24p_u%5E%7B%5Crm%7BIgG%7D%7D%5Cleft(c%5Cright)%20%3D%20%5Cfrac%7B1%7D%7B1%20%2B%20%5Cfrac%7Bc%7D%7Bf_%7B%5Crm%7Bmut%7D%7D%20%5Ctimes%20K_D%7D%5Cleft(2%20%2B%20%5Cfrac%7Bc_%7B%5Crm%7Beff%7D%7D%7D%7Bf_%7B%5Crm%7Bmut%7D%7D%20%5Ctimes%20K_D%7D%5Cright)%7D.%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Complication%3A%20accounting%20for%20ligand%20depletion%0A%20%20%20%20Everything%20above%20is%20written%20in%20terms%20of%20the%20antibody%20(Fab%20or%20IgG)%20concentration%20%24c%24.%0A%20%20%20%20In%20fact%2C%20%24c%24%20in%20these%20equations%20refers%20to%20the%20_free_%20concentration%20of%20antibody.%0A%20%20%20%20But%20when%20the%20concentration%20of%20viral%20antigen%20is%20comparable%20to%20or%20exceeds%20the%20dissociation%20constant%20of%20the%20antibody%20(%24K_D%24%20for%20the%20Fab%20and%20%24K_D%5E%7B%5Crm%7BIgG%7D%7D%24%20for%20the%20IgG)%2C%20there%20can%20be%20%22ligand%20depletion%2C%22%20which%20is%20the%20term%20we%20use%20here%20for%20when%20a%20large%20fraction%20of%20the%20antibody%20is%20bound%20to%20the%20viral%20antigen.%0A%20%20%20%20When%20this%20occurs%2C%20much%20of%20the%20antibody%20gets%20%22soaked%20up%22%20by%20binding%20to%20viral%20antigen%2C%20and%20so%20the%20actual%20concentration%20of%20free%20antibody%20in%20the%20neutralization%20assay%20represented%20by%20%24c%24%20in%20the%20above%20equations%20is%20appreciably%20less%20than%20the%20total%20concentration%20added%20to%20the%20reaction.%0A%20%20%20%20This%20phenomenon%20is%20nicely%20described%20in%20detail%20by%20%5BJarmoskaite%20et%20al%20(2020)%5D(https%3A%2F%2Fdoi.org%2F10.7554%2FeLife.57264)%2C%20who%20refer%20to%20it%20as%20the%20%22titration%20regime.%22%0A%0A%20%20%20%20To%20quantitatively%20deal%20with%20this%2C%20we%20need%20to%20define%20some%20additional%20variables.%0A%20%20%20%20Let%20%24c_%7B%5Crm%7Btotal%7D%7D%24%20be%20the%20total%20antibody%20concentration%20added%20to%20the%20assay%20volume%2C%20let%20%24c_%7B%5Crm%7Bfree%7D%7D%24%20be%20the%20concentration%20of%20antibody%20that%20is%20not%20bound%20to%20viral%20antigen%20(free)%2C%20and%20let%20%24c_%7B%5Crm%7Bbound%7D%7D%24%20be%20the%20concentration%20of%20antibody%20that%20is%20bound%20to%20viral%20antigen.%0A%20%20%20%20Also%2C%20let%20%24v_%7B%5Crm%7Btotal%7D%7D%24%20be%20the%20total%20concentration%20of%20viral%20antigen%20(quantified%20in%20terms%20of%20number%20of%20the%20relevant%20epitope%20per%20unit%20volume)%2C%20let%20%24v_%7B%5Crm%7Bfree%7D%7D%24%20be%20the%20concentration%20of%20free%20viral%20antigen%20(not%20bound%20by%20antibody)%2C%20and%20note%20that%20concentration%20of%20bound%20viral%20antigen%20is%20%24c_%7B%5Crm%7Bbound%7D%7D%24.%0A%20%20%20%20We%20have%20the%20following%20equations%3A%0A%20%20%20%20%24c_%7B%5Crm%7Btotal%7D%7D%20%3D%20c_%7B%5Crm%7Bfree%7D%7D%20%2B%20c_%7B%5Crm%7Bbound%7D%7D%24%20(mass%20balance%20for%20the%20antibody)%2C%0A%20%20%20%20%24v_%7B%5Crm%7Btotal%7D%7D%20%3D%20v_%7B%5Crm%7Bfree%7D%7D%20%2B%20c_%7B%5Crm%7Bbound%7D%7D%24%20(mass%20balance%20for%20the%20viral%20antigen)%2C%20and%20%24c_%7B%5Crm%7Bbound%7D%7D%20K_D%5E%7B%5Crm%7Beffective%7D%7D%20%3D%20v_%7B%5Crm%7Bfree%7D%7D%20c_%7B%5Crm%7Bfree%7D%7D%24%20(there%20is%20an%20equilibrium%20with%20respect%20to%20the%20free%20antibody%20and%20antigen%20concentration).%20Note%20that%20for%20the%20Fab%2C%20%24K_D%5E%7B%5Crm%7Beffective%7D%7D%20%3D%20K_D%24%20and%20for%20the%20IgG%2C%20%24K_D%5E%7B%5Crm%7Beffective%7D%7D%20%3D%20K_D%5E%7B%5Crm%7BIgG%7D%7D%24.%0A%20%20%20%20Solving%20this%20system%20of%20three%20equations%20yields%20(%5BJarmoskaite%20et%20al%20(2020)%5D(https%3A%2F%2Fdoi.org%2F10.7554%2FeLife.57264))%0A%20%20%20%20%24c_%7B%5Crm%7Bbound%7D%7D%20%3D%20%5Cfrac%7Bc_%7B%5Crm%7Btotal%7D%7D%20%2B%20v_%7B%5Crm%7Btotal%7D%7D%20%2B%20K_D%5E%7B%5Crm%7Beffective%7D%7D%20-%20%5Csqrt%7B%5Cleft(c_%7B%5Crm%7Btotal%7D%7D%20%2B%20v_%7B%5Crm%7Btotal%7D%7D%20%2B%20K_D%5E%7B%5Crm%7Beffective%7D%7D%5Cright)%5E2%20-%204%20c_%7B%5Crm%7Btotal%7D%7D%20v_%7B%5Crm%7Btotal%7D%7D%7D%7D%7B2%7D%24%0A%20%20%20%20and%20so%0A%20%20%20%20%24c_%7B%5Crm%7Bfree%7D%7D%20%3D%20c_%7B%5Crm%7Btotal%7D%7D%20-%20c_%7B%5Crm%7Bbound%7D%7D%20%3D%20%5Cfrac%7Bc_%7B%5Crm%7Btotal%7D%7D%20-%20v_%7B%5Crm%7Btotal%7D%7D%20-%20K_D%5E%7B%5Crm%7Beffective%7D%7D%20%2B%20%5Csqrt%7B%5Cleft(c_%7B%5Crm%7Btotal%7D%7D%20%2B%20v_%7B%5Crm%7Btotal%7D%7D%20%2B%20K_D%5E%7B%5Crm%7Beffective%7D%7D%5Cright)%5E2%20-%204%20c_%7B%5Crm%7Btotal%7D%7D%20v_%7B%5Crm%7Btotal%7D%7D%7D%7D%7B2%7D.%24%0A%0A%20%20%20%20Note%20that%20in%20the%20limit%20where%20the%20viral%20antigen%20concentration%20is%20limiting%20and%20so%20we%20are%20outside%20the%20ligand%20depletion%20regime%20(eg%2C%20%24v_%7B%5Crm%7Btotal%7D%7D%20%5Cll%20K_D%5E%7B%5Crm%7Beffective%7D%7D%24)%2C%20then%20we%20just%20have%20%24c_%7B%5Crm%7Bfree%7D%7D%20%5Capprox%20c_%7B%5Crm%7Btotal%7D%7D%24.%0A%20%20%20%20However%2C%20when%20the%20viral%20antigen%20concentration%20is%20comparable%20to%20or%20greater%20than%20%24K_D%5E%7B%5Crm%7Beffective%7D%7D%24%2C%20then%20it%20is%20important%20to%20substitute%20%24c_%7B%5Crm%7Bfree%7D%7D%24%20for%20%24c%24%20in%20all%20of%20the%20equations%20above.%0A%0A%20%20%20%20We%20can%20also%20calculate%20the%20actual%20observed%20IC50s%20accounting%20for%20ligand%20depletion.%0A%20%20%20%20The%20IC50%20(under%20our%20assumption%20above%20that%20this%20is%20when%20half%20of%20viral%20epitopes%20are%20bound)%20occurs%20when%20%24%5Cfrac%7B1%7D%7B2%7D%20%3D%20%5Cfrac%7Bc_%7B%5Crm%7Bbound%7D%7D%7D%7Bv_%7B%5Crm%7Btotal%7D%7D%7D.%24%0A%20%20%20%20It%20turns%20out%20that%20this%20can%20be%20neatly%20solved%20to%20determine%20the%20actual%20observed%20IC50%20(accounting%20for%20ligand%20depletion)%20is%0A%20%20%20%20%24c_%7B%5Crm%7B%7BIC50%2Cobserved%7D%7D%7D%3DK_D%5E%7B%5Crm%7Beffective%7D%7D%20%2B%20%5Cfrac%7Bv_%7B%5Crm%7Btotal%7D%7D%7D%7B2%7D%2C%24%0A%20%20%20%20whereas%20the%20ideal%20IC50%20if%20there%20is%20no%20ligand%20depletion%20(when%20%24v_%7B%5Crm%7Btotal%7D%7D%20%5Cll%20K_D%5E%7B%5Crm%7Beffective%7D%7D%24)%20is%20just%0A%20%20%20%20%24c_%7B%5Crm%7BIC50%2Cideal%7D%7D%20%3D%20K_D%5E%7B%5Crm%7Beffective%7D%7D.%24%0A%0A%20%20%20%20The%20floor%20on%20the%20measurable%20IC50%20(eg%2C%20the%20lowest%20value%20that%20can%20be%20measured)%20is%20%24%5Csim%20v_%7B%5Crm%7Btotal%7D%7D%20%2F%202%24.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Some%20realistic%20parameter%20estimates%0A%0A%20%20%20%20We%20ballpark%20(these%20are%20rough%20estimates)%20realistic%20parameter%20values%20for%20Nirsevimab%20and%20RSV%20F%20from%20our%20experiments.%0A%20%20%20%20For%20the%20Fab%20IC50%2C%20which%20is%20represented%20by%20%24m_%7B%5Crm%7BFab%7D%7D%24%20or%20%24K_D%24%20in%20the%20above%20equations%3A%0A%20%20%20%20%20%20-%20subgroup%20A%20(Long)%20strain%3A%20%24K_D%20%5Csim%200.01%24%20nM%0A%20%20%20%20%20%20-%20subgroup%20B%20(B1)%20strain%3A%20%24K_D%20%5Csim%201%24%20nM%0A%0A%20%20%20%20We%20also%20need%20to%20estimate%20%24c_%7B%5Crm%7Beff%7D%7D%24%3B%20however%2C%20doing%20that%20is%20complicated%20by%20the%20fact%20that%20at%20least%20for%20subgroup%20A%2C%20the%20IgG%20neutralization%20assays%20are%20likely%20in%20the%20ligand%20depletion%20range%20(what%20%5BJarmoskaite%20et%20al%20(2020)%5D(https%3A%2F%2Felifesciences.org%2Farticles%2F57264)%20call%20the%20%22titration%20regime%22)%20where%20the%20IgG%20IC50%20(%24m_%7B%5Crm%7BIgG%7D%7D%24)%20cannot%20be%20measured%20accurately%20since%20the%20concentration%20of%20the%20viral%20antigen%20protein%20likely%20is%20comparable%20or%20greater%20to%20the%20true%20IgG%20IC50.%0A%20%20%20%20So%20we%20will%20use%20neutralization%20measurements%20against%20the%20subgroup%20B%20(B1)%20strain%20as%20any%20IgG%20ligand%20depletion%20should%20be%20less%20here%20due%20to%20the%20lower%20potency%2C%20and%20we%20assume%20that%20the%20actual%20avidity%20(potential%20for%20bivalent%20binding)%20captured%20in%20%24c_%7B%5Crm%7Beff%7D%7D%24%20should%20not%20be%20strain%20dependent.%0A%20%20%20%20Against%20this%20strain%2C%20we%20have%20%24m_%7B%5Crm%7BIgG%7D%7D%20%5Csim%200.01%24%20nM%20and%20%24m_%7B%5Crm%7BFab%7D%7D%20%5Csim%201%24%20nM.%0A%20%20%20%20So%20this%20gives%20%24c_%7B%5Crm%7Beff%7D%7D%20%5Csim%20100%24%20nM.%0A%20%20%20%20However%2C%20there%20is%20probably%20still%20ligand%20depletion%20in%20the%20measurement%20of%20%24m_%7B%5Crm%7BIgG%7D%7D%24%20since%20the%20subtype%20B%20IgG%20neutralization%20is%20just%20as%20good%20as%20subtype%20A%2C%20so%20we%20will%20add%20another%20factor%20of%2010%20and%20use%20%24c_%7B%5Crm%7Beff%7D%7D%20%5Csim%201000%24%20nM%20(note%20this%20remains%20a%20very%20rough%20estimate%20and%20real%20value%20could%20be%20higher).%0A%0A%20%20%20%20The%20hardest%20parameter%20to%20estimate%20is%20the%20number%20of%20viral%20epitopes%20%24v_%7B%5Crm%7Btotal%7D%7D%24%20which%20determines%20if%20there%20is%20ligand%20depletion.%0A%20%20%20%20Roughly%2C%20we%20might%20estimate%20%2410%5E6%24%20infectious%20particles%20per%20ml%2C%20with%20%2410%5E2%24%20non-infectious%20particles%20for%20each%20infectious%20one%2C%20%2410%5E2%24%20epitopes%20per%20particle.%0A%20%20%20%20In%20that%20case%2C%20we%20have%20%2410%5E%7B13%7D%24%20epitopes%20%2F%20liter%2C%20which%20gives%20%24v_%7B%5Crm%7Btotal%7D%7D%20%5Csim%200.01%24%20nM.%0A%20%20%20%20Note%20that%20this%20is%20very%20much%20a%20ballpark%20estimate%2C%20and%20could%20vary%20by%20an%20order%20of%20magnitude%20or%20more%20in%20either%20direction.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Interactive%20visualization%20of%20IgG%20and%20Fab%20neutralization%20and%20how%20it%20is%20affected%20by%20mutations%0A%0A%20%20%20%20We%20now%20plot%20interactive%20visualizations%20initialized%20with%20the%20ballpark-realistic%20parameter%20values%20above%20for%20the%20subgroup%20A%20strain%20of%20how%20nirsevimab%20neutralization%20differs%20between%20the%20Fab%20and%20IgG%20and%20how%20this%20is%20impacted%20by%20mutations.%0A%20%20%20%20In%20the%20interactive%20chart%20below%20and%20also%20at%20%5Bhttps%3A%2F%2Fjbloomlab.github.io%2FIgG-vs-Fab-neutralization%2Fchart.html%5D(https%3A%2F%2Fjbloomlab.github.io%2FIgG-vs-Fab-neutralization%2Fchart.html)%2C%20you%20can%20use%20the%20sliders%20to%20adjust%20the%20Fab%20affinity%20(%24K_D%24)%2C%20the%20avidity%20(%24c_%7B%5Crm%7Beff%7D%7D%24)%2C%20the%20effect%20of%20the%20viral%20mutation(s)%2C%20and%20the%20viral%20epitope%20concentration.%0A%20%20%20%20As%20you%20do%20so%2C%20you%20can%20see%20how%20the%20neutralization%20curves%20and%20IC50s%20change.%0A%20%20%20%20Note%20for%20the%20IC50s%20it%20shows%20both%20the%20actual%20observed%20IC50%20(accounting%20for%20ligand%20depletion)%20as%20well%20as%20what%20would%20be%20the%20ideal%20IC50%20in%20the%20absence%20of%20any%20ligand%20depletion.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20math%0A%0A%20%20%20%20import%20altair%20as%20alt%0A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20import%20numpy%0A%0A%20%20%20%20import%20pandas%20as%20pd%0A%0A%20%20%20%20%23%20---%20Data%20over%20concentration%20(nM)%20on%20a%20log%20grid%20---%0A%20%20%20%20concentrations%20%3D%20numpy.logspace(-4%2C%203%2C%20400)%20%20%20%23%200.001%20nM%20to%201000%20nM%0A%20%20%20%20df%20%3D%20pd.DataFrame(%7B%22c%22%3A%20concentrations%7D)%0A%0A%20%20%20%20%23%20---%20Interactive%20parameters%20---%0A%20%20%20%20log10_KD%20%3D%20alt.param(%0A%20%20%20%20%20%20%20%20name%3D%22log10_KD%22%2C%0A%20%20%20%20%20%20%20%20value%3D-2%2C%0A%20%20%20%20%20%20%20%20bind%3Dalt.binding_range(%0A%20%20%20%20%20%20%20%20%20%20%20%20min%3D-3%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20max%3D3%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20step%3D0.1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Fab%20neutralization%20potency%3A%20log10%20KD%20(nM)%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20log10_c_eff%20%3D%20alt.param(%0A%20%20%20%20%20%20%20%20name%3D%22log10_c_eff%22%2C%0A%20%20%20%20%20%20%20%20value%3D3%2C%0A%20%20%20%20%20%20%20%20bind%3Dalt.binding_range(%0A%20%20%20%20%20%20%20%20%20%20%20%20min%3D-2%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20max%3D5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20step%3D0.1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22avidity%20for%20IgG%3A%20log10%20c_eff%20(nM)%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20log10_f_mut%20%3D%20alt.param(%0A%20%20%20%20%20%20%20%20name%3D%22log10_f_mut%22%2C%0A%20%20%20%20%20%20%20%20value%3D1.3%2C%0A%20%20%20%20%20%20%20%20bind%3Dalt.binding_range(%0A%20%20%20%20%20%20%20%20%20%20%20%20min%3D-1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20max%3D3%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20step%3D0.1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22viral%20mutation%20impact%20on%20Fab%20neutralization%20(fold%20change)%3A%20log10%20f_mut%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%0A%20%20%20%20log10_v_total%20%3D%20alt.param(%0A%20%20%20%20%20%20%20%20name%3D%22log10_v_total%22%2C%0A%20%20%20%20%20%20%20%20value%3D-2%2C%0A%20%20%20%20%20%20%20%20bind%3Dalt.binding_range(%0A%20%20%20%20%20%20%20%20%20%20%20%20min%3D-5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20max%3D0%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20step%3D0.1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22viral%20epitope%20concentration%20(nM)%3A%20log10%20v_total%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20formatting%20for%20different%20antibody%20types%0A%20%20%20%20line_format%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22Fab%22%3A%20(%22%231f77b4%22%2C%20%5B1%2C%200%5D)%2C%0A%20%20%20%20%20%20%20%20%22Fab%20mutant%22%3A%20(%22%236baed6%22%2C%20%5B2%2C%202%5D)%2C%0A%20%20%20%20%20%20%20%20%22IgG%22%3A%20(%22%23ff7f0e%22%2C%20%5B1%2C%200%5D)%2C%0A%20%20%20%20%20%20%20%20%22IgG%20mutant%22%3A%20(%22%23fdae6b%22%2C%20%5B2%2C%202%5D)%2C%0A%20%20%20%20%7D%0A%0A%20%20%20%20%23%20Build%20the%20base%20chart%0A%20%20%20%20chart_base%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart(df)%0A%20%20%20%20%20%20%20%20.add_params(log10_KD%2C%20log10_c_eff%2C%20log10_f_mut%2C%20log10_v_total)%0A%20%20%20%20%20%20%20%20.transform_calculate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20first%20get%20logged%20parameters%20to%20data%20fields%20that%20can%20be%20used%0A%20%20%20%20%20%20%20%20%20%20%20%20KD%3D%22pow(10%2C%20log10_KD)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20c_eff%3D%22pow(10%2C%20log10_c_eff)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20f_mut%3D%22pow(10%2C%20log10_f_mut)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20v_total%3D%22pow(10%2C%20log10_v_total)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Now%20get%20the%20KD%20effective%20for%20antibody-virus%20pairs%0A%20%20%20%20%20%20%20%20%20%20%20%20Fab%3Dalt.datum.KD%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20**%7B%22Fab%20mutant%22%3A%20alt.datum.KD%20*%20alt.datum.f_mut%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20IgG%3Dalt.datum.KD%20%2F%20(2%20%2B%20alt.datum.c_eff%20%2F%20alt.datum.KD)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20**%7B%22IgG%20mutant%22%3A%20alt.datum%5B%22Fab%20mutant%22%5D%20%2F%20(2%20%2B%20alt.datum.c_eff%20%2F%20alt.datum%5B%22Fab%20mutant%22%5D)%7D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.transform_fold(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%22Fab%22%2C%20%22Fab%20mutant%22%2C%20%22IgG%22%2C%20%22IgG%20mutant%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%22antibody%20type%22%2C%20%22KD_eff%22%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.transform_calculate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20calculate%20free%20concentrations%0A%20%20%20%20%20%20%20%20%20%20%20%20c_free%3D(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(alt.datum.c%20-%20alt.datum.v_total%20-%20alt.datum.KD_eff)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2B%20alt.expr.sqrt(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(alt.datum.c%20%2B%20alt.datum.v_total%20%2B%20alt.datum.KD_eff)**2%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%204%20*%20alt.datum.c%20*%20alt.datum.v_total%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%20%2F%202.0%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20calculate%20fractions%20unbound%20for%20mutated%20and%20unmutated%20Fab%20and%20IgG%0A%20%20%20%20%20%20%20%20%20%20%20%20fraction_infectivity%3D1%20%2F%20(1%20%2B%20alt.datum.c_free%20%2F%20alt.datum.KD_eff)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20build%20chart%20showing%20IC50s%0A%20%20%20%20ic50_chart%20%3D%20(%0A%20%20%20%20%20%20%20%20chart_base%0A%20%20%20%20%20%20%20%20.transform_aggregate(%0A%20%20%20%20%20%20%20%20%20%20%20%20ideal%3D%22mean(KD_eff)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mean_v_total%3D%22mean(v_total)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20groupby%3D%5B%22antibody%20type%22%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.transform_calculate(%0A%20%20%20%20%20%20%20%20%20%20%20%20observed%3Dalt.datum.ideal%20%2B%20alt.datum.mean_v_total%20%2F%202%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.transform_fold(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%22ideal%22%2C%20%22observed%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%22IC50%20type%22%2C%20%22IC50%22%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.X(%22antibody%20type%3AN%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.Y(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22IC50%3AQ%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dalt.Scale(type%3D%22log%22%2C%20nice%3DFalse%2C%20padding%3D10)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22IC50%20(nM)%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.Fill(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22antibody%20type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dalt.Scale(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20domain%3Dline_format.keys()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20range%3D%5Btup%5B0%5D%20for%20tup%20in%20line_format.values()%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20legend%3DNone%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.Shape(%22IC50%20type%3AN%22%2C%20legend%3Dalt.Legend(symbolSize%3D170))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22antibody%20type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22IC50%3AQ%22%2C%20title%3D%22IC50%20(nM)%22%2C%20format%3D%22.2g%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22IC50%20type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.mark_point(size%3D170%2C%20stroke%3D%22black%22%2C%20strokeWidth%3D1%2C%20opacity%3D0.75)%0A%20%20%20%20%20%20%20%20.properties(height%3D250%2C%20width%3D130)%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Build%20the%20neutralization%20chart%0A%20%20%20%20neut_chart%20%3D%20(%0A%20%20%20%20%20%20%20%20chart_base%0A%20%20%20%20%20%20%20%20.mark_line(size%3D5)%0A%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.X(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22c%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22antibody%20concentration%20(nM)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dalt.Scale(type%3D%22log%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.Y(%22fraction_infectivity%3AQ%22%2C%20title%3D%22fraction%20infectivity%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.Color(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22antibody%20type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dalt.Scale(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20domain%3Dline_format.keys()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20range%3D%5Btup%5B0%5D%20for%20tup%20in%20line_format.values()%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20legend%3Dalt.Legend(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20orient%3D%22bottom%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20titleOrient%3D%22left%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20symbolStrokeWidth%3D5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20symbolSize%3D700%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.StrokeDash(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22antibody%20type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dalt.Scale(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20domain%3Dline_format.keys()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20range%3D%5Btup%5B1%5D%20for%20tup%20in%20line_format.values()%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22c%22%2C%20title%3D%22concentration%20(nM)%22%2C%20format%3D%22.2g%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22fraction_infectivity%3AQ%22%2C%20title%3D%22fraction%20infectivity%22%2C%20format%3D%22.2g%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22antibody%20type%3AN%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22c_free%3AQ%22%2C%20title%3D%22free%20concentration%20(nM)%22%2C%20format%3D%22.2g%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.properties(width%3D600%2C%20height%3D320)%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20chart%20showing%20floor%20on%20measurable%20IC%0A%20%20%20%20neut_floor%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart()%0A%20%20%20%20%20%20%20%20.add_params(log10_v_total)%0A%20%20%20%20%20%20%20%20.transform_calculate(floor_IC50%3D%22pow(10%2C%20log10_v_total)%20%2F%202%22)%0A%20%20%20%20%20%20%20%20.mark_rule(strokeDash%3D%5B8%2C%208%5D%2C%20color%3D%22gray%22%2C%20size%3D4%2C%20opacity%3D0.5)%0A%20%20%20%20%20%20%20%20.encode(alt.X(%22floor_IC50%3AQ%22))%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20floor%20on%20measurable%20IC50%0A%20%20%20%20ic50_floor%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart()%0A%20%20%20%20%20%20%20%20.add_params(log10_v_total)%0A%20%20%20%20%20%20%20%20.transform_calculate(IC50%3D%22pow(10%2C%20log10_v_total)%20%2F%202%22)%0A%20%20%20%20%20%20%20%20.mark_rule(strokeDash%3D%5B8%2C%208%5D%2C%20color%3D%22gray%22%2C%20size%3D4%2C%20opacity%3D0.5)%0A%20%20%20%20%20%20%20%20.encode(alt.Y(%22IC50%3AQ%22))%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20add%20a%20subtitle%20with%20hyperlink%0A%20%20%20%20hyperlink_subtitle%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart(%0A%20%20%20%20%20%20%20%20%20%20%20%20pd.DataFrame(%5B%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22text%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22https%3A%2F%2Fjbloomlab.github.io%2FIgG-vs-Fab-neutralization%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22url%22%3A%20%22https%3A%2F%2Fjbloomlab.github.io%2FIgG-vs-Fab-neutralization%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%5D)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20.mark_text(baseline%3D%22bottom%22%2C%20align%3D%22left%22%2C%20size%3D16)%0A%20%20%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20text%3D%22text%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20href%3D%22url%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dalt.value(%22%231a0dab%22)%2C%0A%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20.properties(height%3D20)%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20combine%20charts%0A%20%20%20%20chart%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.vconcat(%0A%20%20%20%20%20%20%20%20%20%20%20%20hyperlink_subtitle%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20((neut_chart%20%2B%20neut_floor)%20%7C%20(ic50_chart%20%2B%20ic50_floor))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20spacing%3D5%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.resolve_scale(%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22independent%22%2C%20strokeDash%3D%22independent%22%2C%20shape%3D%22independent%22%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.properties(%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3Dalt.TitleParams(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22IgG%20versus%20Fab%20neutralization%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20subtitle%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Left%20plot%20shows%20observed%20neutralization%20curves%20against%20virus%20and%20virus%20mutant%20by%20IgG%20and%20Fab.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Right%20plot%20shows%20the%20observed%20IC50s%2C%20as%20well%20as%20ideal%20IC50s%20if%20there%20was%20no%20ligand%20depletion.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Dashed%20gray%20line%20on%20both%20plots%20is%20floor%20on%20observed%20IC50%20due%20to%20ligand%20depletion.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Adjust%20Fab%20potency%2C%20IgG%20avidity%2C%20mutation%20effect%2C%20and%20viral%20epitope%20concentration%20using%20options%20at%20bottom.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fontSize%3D24%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20subtitleFontSize%3D16%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20offset%3D2%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dx%3D70%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.configure_axis(titleFontSize%3D22%2C%20labelFontSize%3D18%2C%20grid%3DFalse)%0A%20%20%20%20%20%20%20%20.configure_legend(titleFontSize%3D22%2C%20labelFontSize%3D22)%0A%20%20%20%20)%0A%0A%20%20%20%20chart.save(%22chart.html%22)%0A%0A%20%20%20%20chart%0A%20%20%20%20return%20mo%2C%20numpy%2C%20pd%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Plot%20model-simulated%20and%20actual%20RSV%20F%20neutralization%20data%20side%20by%20side%0A%20%20%20%20The%20plot%20below%20shows%3A%0A%0A%20%20%20%20-%20**Left%20panels%20(simulated)%3A**%20Neutralization%20curves%20from%20the%20biophysical%20model%20above%2C%20illustrating%20how%20IgG%20versus%20Fab%20differences%20depend%20on%20binding%20affinity.%20In%20the%20simulated%20model%20data%20we%20use%20the%20realistic%20parameter%20estimates%20from%20above%20and%20a%20Fab%20%24K_D%24%20of%200.01%20nM%20for%20the%20high-affinity%20strain%20(mirroring%20subtype%20A)%20and%20of%201%20nM%20for%20the%20low-affinity%20strain%20(mirroring%20subtype%20B).%0A%20%20%20%20-%20**Right%20panels%20(actual%20data)%3A**%20Pseudovirus%20neutralization%20assay%20data%20from%20Teagan%20and%20Cassie%20using%20Long%20(subtype%20A)%20or%20B1%20(subtype%20B)%20RSV%20F%20proteins%20with%20escape%20mutations%20K68Q%2C%20K201S%2C%20and%20N201S.%0A%0A%20%20%20%20Note%20that%20a%20high-resolution%20version%20of%20the%20plot%20is%20saved%20to%20%5Bhttps%3A%2F%2Fgithub.com%2Fjbloomlab%2FIgG-vs-Fab-neutralization%2Fblob%2Fmain%2Fsimulated_vs_actual_curves.svg%5D(https%3A%2F%2Fgithub.com%2Fjbloomlab%2FIgG-vs-Fab-neutralization%2Fblob%2Fmain%2Fsimulated_vs_actual_curves.svg).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(numpy%2C%20pd)%3A%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20matplotlib.font_manager%20as%20fm%0A%20%20%20%20from%20matplotlib.gridspec%20import%20GridSpec%0A%20%20%20%20import%20neutcurve%0A%0A%20%20%20%20%23%20Register%20local%20Comic%20Neue%20fonts%20for%20xkcd%20style%20(more%20readable%20than%20Humor%20Sans)%0A%20%20%20%20fm.fontManager.addfont(%22fonts%2FComicNeue-Regular.ttf%22)%0A%20%20%20%20fm.fontManager.addfont(%22fonts%2FComicNeue-Bold.ttf%22)%0A%20%20%20%20%23%20Register%20Open%20Sans%20fonts%20(has%20semibold%20weight)%20for%20actual%20data%0A%20%20%20%20fm.fontManager.addfont(%22fonts%2FOpenSans-Regular.ttf%22)%0A%20%20%20%20fm.fontManager.addfont(%22fonts%2FOpenSans-SemiBold.ttf%22)%0A%20%20%20%20fm.fontManager.addfont(%22fonts%2FOpenSans-Bold.ttf%22)%0A%20%20%20%20%23%20Rebuild%20font%20cache%20to%20ensure%20fonts%20are%20found%0A%20%20%20%20fm._load_fontmanager(try_read_cache%3DFalse)%0A%0A%20%20%20%20%23%20%3D%3D%3D%3D%3D%20Read%20and%20prepare%20real%20data%20%3D%3D%3D%3D%3D%0A%20%20%20%20real_data%20%3D%20pd.read_csv(%22actual_RSV-F_data.csv%22)%0A%0A%20%20%20%20data_to_plot%20%3D%20(%0A%20%20%20%20%20%20%20%20real_data%0A%20%20%20%20%20%20%20%20.query(%22date%20in%20%5B'2025-08-28'%2C%20'2025-10-02'%5D%22)%0A%20%20%20%20%20%20%20%20.assign(%0A%20%20%20%20%20%20%20%20%20%20%20%20antibody%3Dlambda%20x%3A%20x%5B%22serum%22%5D.str.split().str%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20antibody_type%3Dlambda%20x%3A%20x%5B%22serum%22%5D.str.split().str%5B1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20strain%3Dlambda%20x%3A%20x%5B%22virus%22%5D.str.split().str%5B1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20subtype%3Dlambda%20x%3A%20x%5B%22strain%22%5D.map(%7B%22Long%22%3A%20%22A%22%2C%20%22B1%22%3A%20%22B%22%7D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mutant%3Dlambda%20x%3A%20x%5B%22virus%22%5D.str.split().str%5B-1%5D.map(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20lambda%20m%3A%20%22unmutated%22%20if%20m%20%3D%3D%20%22WT%22%20else%20m%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line_type%3Dlambda%20x%3A%20x%5B%22antibody_type%22%5D%20%2B%20%22%20vs%20%22%20%2B%20x%5B%22mutant%22%5D.map(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20lambda%20m%3A%20%22mutant%22%20if%20m%20!%3D%20%22unmutated%22%20else%20%22unmutated%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.query(%22antibody%20%3D%3D%20'Nirsevimab'%22)%0A%20%20%20%20%20%20%20%20.query(%22mutant%20in%20%5B'unmutated'%2C%20'K68Q'%2C%20'K201S'%2C%20'N201S'%5D%22)%0A%20%20%20%20%20%20%20%20.assign(%0A%20%20%20%20%20%20%20%20%20%20%20%20comparison%3Dlambda%20x%3A%20x.apply(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20lambda%20r%3A%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(%5B%22K68Q%22%2C%20%22K201S%22%5D%20if%20r%5B%22subtype%22%5D%20%3D%3D%20%22A%22%20else%20%5B%22K68Q%22%2C%20%22N201S%22%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20r%5B%22mutant%22%5D%20%3D%3D%20%22unmutated%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20%5Br%5B%22mutant%22%5D%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20axis%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.explode(%22comparison%22)%0A%20%20%20%20%20%20%20%20.assign(facet_type%3Dlambda%20x%3A%20%22subtype%20%22%20%2B%20x%5B%22subtype%22%5D%20%2B%20%22%20%C2%B1%20%22%20%2B%20x%5B%22comparison%22%5D)%0A%20%20%20%20)%0A%0A%20%20%20%20fits_to_plot%20%3D%20neutcurve.CurveFits(%0A%20%20%20%20%20%20%20%20data_to_plot%2C%20serum_col%3D%22line_type%22%2C%20virus_col%3D%22facet_type%22%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20%3D%3D%3D%3D%3D%20Generate%20simulated%20data%20%3D%3D%3D%3D%3D%0A%20%20%20%20igg_conc%20%3D%20sorted(data_to_plot.query(%22antibody_type%20%3D%3D%20'IgG'%22)%5B%22concentration%22%5D.unique())%0A%20%20%20%20fab_conc%20%3D%20sorted(data_to_plot.query(%22antibody_type%20%3D%3D%20'Fab'%22)%5B%22concentration%22%5D.unique())%0A%0A%20%20%20%20shift_factors%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22Fab%20vs%20unmutated%22%3A%200.97%2C%0A%20%20%20%20%20%20%20%20%22Fab%20vs%20mutant%22%3A%201.03%2C%0A%20%20%20%20%20%20%20%20%22IgG%20vs%20unmutated%22%3A%200.95%2C%0A%20%20%20%20%20%20%20%20%22IgG%20vs%20mutant%22%3A%201.05%2C%0A%20%20%20%20%7D%0A%0A%20%20%20%20def%20simulate_neut_data(KD%2C%20c_eff%3D1000%2C%20f_mut%3D20%2C%20v_total%3D0.01)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Simulate%20neutralization%20data%20for%20Fab%20and%20IgG%20against%20unmutated%20and%20mutant%20virus.%22%22%22%0A%20%20%20%20%20%20%20%20data%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20for%20antibody_type%2C%20is_igg%20in%20%5B(%22Fab%22%2C%20False)%2C%20(%22IgG%22%2C%20True)%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20concentrations%20%3D%20numpy.array(igg_conc%20if%20is_igg%20else%20fab_conc)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20mutant_status%2C%20fold_change%20in%20%5B(%22unmutated%22%2C%201)%2C%20(%22mutant%22%2C%20f_mut)%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20line_type%20%3D%20f%22%7Bantibody_type%7D%20vs%20%7Bmutant_status%7D%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20shifted_conc%20%3D%20concentrations%20*%20shift_factors%5Bline_type%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20KD_eff%20%3D%20KD%20*%20fold_change%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20is_igg%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20KD_eff%20%3D%20KD_eff%20%2F%20(2%20%2B%20c_eff%20%2F%20KD_eff)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20c_free%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(shifted_conc%20-%20v_total%20-%20KD_eff)%20%2B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20numpy.sqrt((shifted_conc%20%2B%20v_total%20%2B%20KD_eff)**2%20-%204%20*%20shifted_conc%20*%20v_total)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%20%2F%202%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fraction_infectivity%20%3D%201%20%2F%20(1%20%2B%20c_free%20%2F%20KD_eff)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20data.extend(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%22concentration%22%3A%20c%2C%20%22fraction%20infectivity%22%3A%20fi%2C%20%22line_type%22%3A%20line_type%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20c%2C%20fi%20in%20zip(shifted_conc%2C%20fraction_infectivity)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D)%0A%20%20%20%20%20%20%20%20return%20pd.DataFrame(data)%0A%0A%20%20%20%20sim_data%20%3D%20pd.concat(%5B%0A%20%20%20%20%20%20%20%20simulate_neut_data(KD%3D0.01).assign(facet_type%3D%22strain%20bound%20w%20high%20affinity%22)%2C%0A%20%20%20%20%20%20%20%20simulate_neut_data(KD%3D1.0).assign(facet_type%3D%22strain%20bound%20w%20low%20affinity%22)%2C%0A%20%20%20%20%5D).assign(replicate%3D1)%0A%0A%20%20%20%20fits_to_sim%20%3D%20neutcurve.CurveFits(%0A%20%20%20%20%20%20%20%20sim_data%2C%20serum_col%3D%22line_type%22%2C%20virus_col%3D%22facet_type%22%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20%3D%3D%3D%3D%3D%20Define%20styling%20%3D%3D%3D%3D%3D%0A%20%20%20%20lines%20%3D%20%5B%22IgG%20vs%20unmutated%22%2C%20%22IgG%20vs%20mutant%22%2C%20%22Fab%20vs%20unmutated%22%2C%20%22Fab%20vs%20mutant%22%5D%0A%20%20%20%20line_color_markers%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22Fab%20vs%20unmutated%22%3A%20(%22%231f77b4%22%2C%20%22o%22)%2C%0A%20%20%20%20%20%20%20%20%22Fab%20vs%20mutant%22%3A%20(%22%236baed6%22%2C%20%22%5E%22)%2C%0A%20%20%20%20%20%20%20%20%22IgG%20vs%20unmutated%22%3A%20(%22%23ff7f0e%22%2C%20%22o%22)%2C%0A%20%20%20%20%20%20%20%20%22IgG%20vs%20mutant%22%3A%20(%22%23fdae6b%22%2C%20%22%5E%22)%2C%0A%20%20%20%20%7D%0A%20%20%20%20mutant_colors%20%3D%20%5Bv%5B0%5D%20for%20k%2C%20v%20in%20line_color_markers.items()%20if%20%22mutant%22%20in%20k%5D%0A%0A%20%20%20%20%23%20%3D%3D%3D%3D%3D%20Create%20combined%20figure%20with%20GridSpec%20%3D%3D%3D%3D%3D%0A%20%20%20%20%23%20Wider%20panels%20(greater%20width%20to%20height%20ratio)%20with%20space%20for%20legend%20in%20gap%0A%20%20%20%20fig%20%3D%20plt.figure(figsize%3D(15.7%2C%205.25))%0A%20%20%20%20gs%20%3D%20GridSpec(2%2C%204%2C%20figure%3Dfig%2C%20width_ratios%3D%5B1%2C%201.15%2C%201%2C%201%5D%2C%20wspace%3D0.08%2C%20hspace%3D0.28)%0A%0A%20%20%20%20%23%20Create%20axes%3A%20col%200%20%3D%20simulated%2C%20col%201%20%3D%20gap%20(skip)%2C%20cols%202-3%20%3D%20actual%0A%20%20%20%20sim_axes%20%3D%20%5Bfig.add_subplot(gs%5B0%2C%200%5D)%2C%20fig.add_subplot(gs%5B1%2C%200%5D)%5D%0A%20%20%20%20data_axes%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%5Bfig.add_subplot(gs%5B0%2C%202%5D)%2C%20fig.add_subplot(gs%5B0%2C%203%5D)%5D%2C%0A%20%20%20%20%20%20%20%20%5Bfig.add_subplot(gs%5B1%2C%202%5D)%2C%20fig.add_subplot(gs%5B1%2C%203%5D)%5D%2C%0A%20%20%20%20%5D%0A%0A%20%20%20%20%23%20%3D%3D%3D%3D%3D%20Helper%20function%20to%20plot%20curves%20on%20an%20axes%20%3D%3D%3D%3D%3D%0A%20%20%20%20def%20plot_panel(ax%2C%20fits%2C%20virus%2C%20title%2C%20show_yticks%3DFalse%2C%20show_xticks%3DFalse%2C%20ylim%3D(-0.05%2C%201.1)%2C%20fontname%3DNone)%3A%0A%20%20%20%20%20%20%20%20ax.set_xscale(%22log%22)%0A%20%20%20%20%20%20%20%20for%20line_type%20in%20lines%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20curve%20%3D%20fits.getCurve(serum%3Dline_type%2C%20virus%3Dvirus%2C%20replicate%3D%22average%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20color%2C%20marker%20%3D%20line_color_markers%5Bline_type%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20linestyle%20%3D%20%22--%22%20if%20color%20in%20mutant_colors%20else%20%22-%22%0A%20%20%20%20%20%20%20%20%20%20%20%20curve.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ax%3Dax%2C%20color%3Dcolor%2C%20marker%3Dmarker%2C%20markersize%3D8%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20linewidth%3D1.75%2C%20linestyle%3Dlinestyle%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3DNone%2C%20ylabel%3DNone%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%23%20Re-enable%20autoscale%20after%20plotting%20and%20recalculate%20limits%0A%20%20%20%20%20%20%20%20ax.autoscale(True%2C%20%22both%22)%0A%20%20%20%20%20%20%20%20ax.relim()%0A%20%20%20%20%20%20%20%20ax.autoscale_view()%0A%20%20%20%20%20%20%20%20ax.set_title(title%2C%20fontsize%3D16%2C%20fontname%3Dfontname)%0A%20%20%20%20%20%20%20%20ax.set_xlabel(%22%22)%0A%20%20%20%20%20%20%20%20ax.set_ylabel(%22%22)%0A%20%20%20%20%20%20%20%20if%20not%20show_xticks%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ax.tick_params(labelbottom%3DFalse)%0A%20%20%20%20%20%20%20%20if%20not%20show_yticks%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ax.tick_params(labelleft%3DFalse)%0A%20%20%20%20%20%20%20%20ax.tick_params(labelsize%3D12)%0A%20%20%20%20%20%20%20%20ax.set_yticks(%5B0%2C%200.5%2C%201%5D)%0A%20%20%20%20%20%20%20%20ax.set_ylim(ylim)%0A%20%20%20%20%20%20%20%20%23%20Set%20font%20for%20tick%20labels%20if%20specified%0A%20%20%20%20%20%20%20%20if%20fontname%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20label%20in%20ax.get_xticklabels()%20%2B%20ax.get_yticklabels()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20label.set_fontname(fontname)%0A%0A%20%20%20%20%23%20%3D%3D%3D%3D%3D%20Plot%20simulated%20data%20(in%20xkcd%20style)%20%3D%3D%3D%3D%3D%0A%20%20%20%20sim_viruses%20%3D%20%5B%22strain%20bound%20w%20high%20affinity%22%2C%20%22strain%20bound%20w%20low%20affinity%22%5D%0A%20%20%20%20with%20plt.xkcd()%3A%0A%20%20%20%20%20%20%20%20%23%20Set%20Comic%20Neue%20as%20font%20within%20xkcd%20context%0A%20%20%20%20%20%20%20%20plt.rcParams%5B'font.family'%5D%20%3D%20'Comic%20Neue'%0A%20%20%20%20%20%20%20%20for%20i%2C%20virus%20in%20enumerate(sim_viruses)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20plot_panel(sim_axes%5Bi%5D%2C%20fits_to_sim%2C%20virus%2C%20virus%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20show_yticks%3DTrue%2C%20show_xticks%3D(i%20%3D%3D%201)%2C%20fontname%3D'Comic%20Neue')%0A%20%20%20%20%20%20%20%20%23%20Add%20title%20and%20axis%20labels%20for%20simulated%20data%20in%20xkcd%20font%0A%20%20%20%20%20%20%20%20fig.text(0.21%2C%200.98%2C%20%22biophysical%20model%20(simulated)%22%2C%20ha%3D%22center%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fontsize%3D18%2C%20fontweight%3D%22bold%22%2C%20fontname%3D'Comic%20Neue')%0A%20%20%20%20%20%20%20%20fig.text(0.09%2C%200.5%2C%20%22fraction%20viral%20infectivity%22%2C%20va%3D%22center%22%2C%20ha%3D%22center%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rotation%3D90%2C%20fontsize%3D17%2C%20fontname%3D'Comic%20Neue')%0A%20%20%20%20%20%20%20%20fig.text(0.22%2C%200.02%2C%20%22antibody%20concentration%20(nM)%22%2C%20ha%3D%22center%22%2C%20fontsize%3D17%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fontname%3D'Comic%20Neue')%0A%0A%20%20%20%20%23%20%3D%3D%3D%3D%3D%20Plot%20actual%20data%20%3D%3D%3D%3D%3D%0A%20%20%20%20%23%20Use%20slightly%20larger%20y-axis%20max%20to%20accommodate%20data%20points%20above%201.0%0A%20%20%20%20actual_ylim%20%3D%20(-0.05%2C%201.25)%0A%20%20%20%20actual_viruses%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%5B%22subtype%20A%20%C2%B1%20K68Q%22%2C%20%22subtype%20A%20%C2%B1%20K201S%22%5D%2C%0A%20%20%20%20%20%20%20%20%5B%22subtype%20B%20%C2%B1%20K68Q%22%2C%20%22subtype%20B%20%C2%B1%20N201S%22%5D%2C%0A%20%20%20%20%5D%0A%20%20%20%20for%20i%2C%20row%20in%20enumerate(actual_viruses)%3A%0A%20%20%20%20%20%20%20%20for%20j%2C%20virus%20in%20enumerate(row)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20plot_panel(data_axes%5Bi%5D%5Bj%5D%2C%20fits_to_plot%2C%20virus%2C%20virus%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20show_yticks%3D(j%20%3D%3D%200)%2C%20show_xticks%3D(i%20%3D%3D%201)%2C%20ylim%3Dactual_ylim%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fontname%3D'Open%20Sans')%0A%0A%20%20%20%20%23%20%3D%3D%3D%3D%3D%20Add%20section%20title%20and%20axis%20labels%20for%20actual%20data%20%3D%3D%3D%3D%3D%0A%20%20%20%20fig.text(0.71%2C%200.98%2C%20%22actual%20RSV%20F%20pseudovirus%20neutralization%20data%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20ha%3D%22center%22%2C%20fontsize%3D17%2C%20fontweight%3D600%2C%20fontname%3D'Open%20Sans')%0A%20%20%20%20fig.text(0.497%2C%200.5%2C%20%22fraction%20viral%20infectivity%22%2C%20va%3D%22center%22%2C%20ha%3D%22center%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20rotation%3D90%2C%20fontsize%3D16%2C%20fontname%3D'Open%20Sans')%0A%20%20%20%20fig.text(0.71%2C%200.02%2C%20%22antibody%20concentration%20(nM)%22%2C%20ha%3D%22center%22%2C%20fontsize%3D16%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20fontname%3D'Open%20Sans')%0A%0A%20%20%20%20%23%20%3D%3D%3D%3D%3D%20Create%20shared%20legend%20(vertical%2C%20in%20the%20gap%20between%20panel%20groups)%20%3D%3D%3D%3D%3D%0A%20%20%20%20handles%20%3D%20%5B%0A%20%20%20%20%20%20%20%20plt.Line2D(%5B0%5D%2C%20%5B0%5D%2C%20color%3Dc%2C%20marker%3Dm%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20linestyle%3D(%22--%22%20if%20c%20in%20mutant_colors%20else%20%22-%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20markersize%3D8%2C%20linewidth%3D1.75)%0A%20%20%20%20%20%20%20%20for%20lt%20in%20lines%0A%20%20%20%20%20%20%20%20for%20c%2C%20m%20in%20%5Bline_color_markers%5Blt%5D%5D%0A%20%20%20%20%5D%0A%20%20%20%20fig.legend(handles%2C%20lines%2C%20loc%3D%22center%22%2C%20ncol%3D1%2C%20fontsize%3D15%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bbox_to_anchor%3D(0.395%2C%200.5))%0A%0A%20%20%20%20fig.tight_layout(rect%3D%5B0.02%2C%200.05%2C%201%2C%200.96%5D)%0A%20%20%20%20plt.savefig(%22simulated_vs_actual_curves.svg%22%2C%20bbox_inches%3D%22tight%22)%0A%20%20%20%20plt.savefig(%22simulated_vs_actual_curves.pdf%22%2C%20bbox_inches%3D%22tight%22)%0A%20%20%20%20fig%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
bb398d67b4ed542140740fbbcbe88f49