From 8d634fb231120c02d5c5d81b45c1e70336beee99 Mon Sep 17 00:00:00 2001 From: essmaw Date: Mon, 6 Jul 2026 11:42:18 +0200 Subject: [PATCH 01/26] chore: remove obsolete data files and JSON records. --- data/FFM.txt | 97 ---- data/MOL.txt | 1113 -------------------------------------- data/SOFTNAME.txt | 78 --- data/SOFTVERS.txt | 59 -- data/STIME.txt | 88 --- data/TEMP.txt | 53 -- data/zenodo_1009027.json | 284 ---------- data/zenodo_34415.json | 92 ---- 8 files changed, 1864 deletions(-) delete mode 100644 data/FFM.txt delete mode 100644 data/MOL.txt delete mode 100644 data/SOFTNAME.txt delete mode 100644 data/SOFTVERS.txt delete mode 100644 data/STIME.txt delete mode 100644 data/TEMP.txt delete mode 100644 data/zenodo_1009027.json delete mode 100644 data/zenodo_34415.json diff --git a/data/FFM.txt b/data/FFM.txt deleted file mode 100644 index 3c263f6..0000000 --- a/data/FFM.txt +++ /dev/null @@ -1,97 +0,0 @@ -charmm36 -martini -amber -tip3p -charmm -gaff -berger -opc -slipids -spc -charmm36m -gromos -gromos 54a7 -trappe -amber99sb-ildn -amoeba+ -gafflipid -gal17 -amber99sb-ildb -c36m -c36mcu -charmm c36 -charmm27 -charmm36 (v. june 2015) -general amber force field -gromos 43a1-s3 -gromos87 -opc3 -opls-aa -rsff2 -sirah -williams 7b -amber bsc1 -amber ff03ws -amber ff14sb -amber lipid17 -c22* -c36m -charmm c36 -compass -ecc-ions -ecc-lipids -ecc-popc -ff99sb-ildn-nmr -ff99sb-ildn-phi -gaff2 -martini 2 -mpipi -opls -poger gromos 53a6_l -pys -williams -'berger -amber ff99sb -amber ff99sb-ildn -amber14 -amber14sb -amoeba -autodock -bind3p -c36m. -charmm 27 -charmm36 forcefield (version july 2020) -charmm36m. -charmm36mw -dang -ff03 -ff03* -ff03w -ff96 -ff99 -ff99sb* -ff99sb-ildn -gap -gdml -generalized amber force field -gromos 53a6 -gromos54a7 -l-opls -lipid14 -lipid17 -martini 2.2p -martini 3 -martini 3.0 -martini cg -opls-aa/m -parm99_lna -parmbsc0 -q4md-cd -rsff1 -schnet -sl -tip -tip4p-ew -tip4p/2005 -trappe-ua -βol15 diff --git a/data/MOL.txt b/data/MOL.txt deleted file mode 100644 index 0dc449c..0000000 --- a/data/MOL.txt +++ /dev/null @@ -1,1113 +0,0 @@ -popc -cholesterol -dppc -nacl -dmtap -dmpc -dopc -na+ -dna -meth -tcr -tlr4 -ace2 -calcium -h-ns -pope -cacl_2 -hno3 -atp -chat -keap1 -md-2 -phospholipid -ppsa -stp1 -actin -fus -gr -h2o -ikk -methane -phosphatidylcholine -cetp -cl- -dlpc -dmso -e2 -e6 -glycans -gq -graphene -msmo -omps -pazepc -pc -pima -ritonavir -shk -vdac1 -acyl -at1r -c11r6 -cellulose -d-serine -e3 -n-c28 -osc -p-ftaa -plp -pmhc -popg -sars-cov-2 spike -tpr -ub -arylamide -cl -cobalt -dimyristoylphosphatidylcholine -dimyristoyltrimethylammoniumpropane -furanose -gara -gpi -kerogen -kv1.3 -mcp -onooh -p53 -pa -pah -pfm1-aap -quercetin -rbd -rutin -tbp -tio2 -ubiquinone -acetonitrile -aβ42 -b-raf -bnnt -bromodomains -chitosan -corilagin -cps -diterpenes -dopamine -entf* -fp-2 -gerab -gtp -hydrogen sulfide -ind -kerogens -lbf14 -lna -mebendazole -methanol -nd -neon -pahs -pedot -pgla -pi -pim -pkng -poly-ubiquitin -qgr motif -rsme -sars-cov-2 spike -septin -sirt2 -teixobactin -ubiquitin -βcd -adp -alkyl -alpo-11 -aβ40 -carbohydrates -cationic lipid -chloroxylenol -chs -epoxy resins -glutamate -hmk -kcl -kit -l-alanine -lopinavir -mannose -mercaptopurine -metal -mgl -ncp -phosphate -pknb -protac -s -saq -spike protein -surfactants -t cell receptor -tetracycline -4a8 -aac -alanine dipeptide -alkane -alkanes -antibody -at-rich dna -benenodin-1 -caprolactam -carbohydrate -ceftazidime -ceramide -chl -congocidine -coronavirus 2019-ncov protease -cr3022 -ctab -depc -dgeba -diterpene -dlipc -e6ap -eea1 -ethanol -exbox4+ -fyve -glucoside -glutamine synthetase -glycoproteins -gsh -gssg -h-fbp21 -hsp90 -hyp -k-ras4b -kinase -lanosterol -lasso peptide -ligand -lysine -lysozyme -mt1 -nds -nh3+ -nrf2 -nucleosome -oh -oxa-163 -oxa-48 -oxyacids -pas -phospholipids -phosphonium -poly-ubiquitins -protacs -rnv66 -rsmz -sars-cov-2 spike protein -sodium -tcrs -tempo -triclosan -urea -zeolite -zeolites -2-iodomelatonin -2-methoxy group -2-tp -3g5u -4ksb -4m1m -4zud -act -agos -aldehydes -antibodies -at-rich dna -bptf -ca -cacl -carbon -carbon-based nanomaterials -carbonyl -cephalosporinase -chalcogen -chol -com -cov2 -cu -cu+ -cx50 -cytochrome c -dag -divalent cations -dlipc -dmc -dme -dspc -dspe -elastin -epoxy resins -ethyl acetate -fa2 -fdts -flavonoid -fmoc -fmoc-aa -gaba -gd3+ -gdp -gdpm -glp-1r -glucose -glutamine -glycerol -glycine receptor -glycolipids -gm1 -go -graphene oxide -h2 -hdm2 -hkv1 -hp -hsmo -hypd -k+ -kras -lanthanide -laptm4b -late endosomal protein -methacycline -methyl -mg -mrsa -na -nanodiamonds -nitrogen oxyanions -no2 -no3 -nps -ots -p-6p -p-glycoprotein -palmitoyl-coa -pdms -peg -perfluorinated alcohols -pfo-bpy -phosphoinositides -pkc -pops -potassium -protease -pyr-1gp -qb site -quinones -rubisco -sars-cov-2 -sds -sirt2 inhibitors -smoothened -sp-b -sp-c -spa -superoxide dismutase 1 -tapc -thiolate -thrombin -titanium dioxide -tricaproin -tris-benzimidazole -v7t1 -vegf -y198n -y204f -1,2-dimyristoyl-sn-glycero-3-phosphocholine -1-palmitoyl-2-oleoyl-sn-glycero-3-phosphocholine -17l -29l -33tfncs -6lu7 -6msm -6vsb -8arg -ace2. -acidic zeolites -ag -aggac -ago -air -amanitin -aminobutyric acid -amph -amyloid -amyloid beta -amyloid fibrils -ansamer amanitin -apoe2 -arylamides -aryldiazonium -au1 -aunp -austdiol -azelaoyl -azobioisostere -barnase -barstar -bax -benzene -betaine -bh4 -biliverdin ix -biocides -bok -boron nitride -btk -bv -c15 -carbapenems -carbon dioxide -carboxyl -cellulose acetate -cholesteryl ester transfer protein -choline acetyltransferase -chromatin -chromonic mesogen -chromophore -cnt -co2 -cr -csra -cx46 -cyclic peptides -cyclodextrin -cytochrome c -dapc -darunavir -dehydratase -dialanine -dihexadecylammonium -dimeric protease -dioleoyl-phosphatidylcholine -dipeptides -dma -dna aptamer -dna gq -dnpc -dodecan-1-ol -e. coli apo -ec -egg-white lysozyme -electrolyte -emre -en2 -erk -ether-o -ethylene -fa1b -fdh -formate dehydrogenase -gaba transporter bgt1 -gasdermin-d -gera -glycan -glycerol -glycosylphosphatidylinositol -gp120 -gppnhp -gqs -grs -gsk3β inhibitors -hematoporphyrin -hiv inhibitors -hiv protein -hkii-n -hmi-1a3 -human receptor ace2 -hydrocarbon -ibuprofen -indinavir -ion -ions -ire1 -kinesin -kras-4b -lam -lanosterol -lasr -lasso peptides -li+ -lipf6 -lm -lnas -macrophage migration inhibitory factor -malate dehydrogenase -mannose -mcl-1 -mdm2 -mea -metalloenzyme -methamphetamine -mfi -montmorillonite -mt1 receptor -mt2 -mt2 receptor -mucinases -n-3-oxododecanoyl homoserine lactone -n-alkanes -n-butylpyridinium tetrafluoroborate -n-dodecane -n1-methylpseudouridine -n6-methyladenosine -ncs -nelfinavir -nh4+ -nitrile butadiene -np -ntd -o-glycan -octaose -octapeptide -olefin-c -orexin receptor -orexin-a -p-divinylbenzene -palmitate -para-sexiphenyl -pcs -pedot:pss -peptides -periplasmic chaperone -peroxynitrous acid -ph-btbt-c10 -phe -phenyl -phenylalanine hydroxylase -phenylalanine-4-hydroxylase -phenytoin -phla -phosphatidylethanolamine -phosphoinositol phosphate -phospholipid translocase -pic -pnipaam -polyamine -polydimethylsiloxane -polyleucine -polystyrene -pop1 -popcs -porphyrins -ppis -pss -pupc -pyranose -pyridinylimidazoles -pyrimidoindole -saquinavir -sars-cov-2 spike protein -sbls -sept2 -sept7 -septins -serine/threonine phosphatase -silf -sod1 -sol -stearoyl-coa desaturase -sty -styrene -superoxide dismutase 1 -tcr-pmhc -tempo-pc -thermolysin -thiq -tipranavir -tma -transcriptional regulator -tri-n-butyl phosphate -tyrosine kinase -ubisemiquinone -unguisin a -villin headpiece -vrc01 -xla -xyloglucan -zwitterionic dmpc -α-conotoxin lsia -α3β2 nachr -(s)-norcoclaurine -1,2-diauroyl-sn-glycero-3-phospocholine -1,2-dioleoyl-sn-glycero-3-phosphocholine -1,2-dipalmitoyl-sn-glycero-3-phospcholine -1,2-dipalmitoyl-sn-glycero-3-phosphatidylcholine -1,4-distyrylbenzene -1-palmitoyl-2-oleoyl-sn-glycero-3-phosphoethanolamine -1afo -1fpy -1h,1h-perfluorinated alcohols -1s,2s)-trans-1,2-cyclohexanediol -2,4,6-trimethylbenzenediazonium -2-mercaptopyridine -2-mp -2-phenylpropane -2-thiopyridone -2019ncov virus protease -2hno3 -2kf7 -2m0b -2mesadp -2ojw -2rjy -3,4-ethylenedioxythiophene -3-hydroxy-4-pyridinone -3bei -3eml -3emn -3lu9 -4efl -4hkr -4ptc -4yay -5e11 -5’-gcggcgcgccgc-3’ -6aki -6bbf -6gn1 -6hyc -6m0j -6o3c -6ssz -6xr8 -7cel -8-aryl -a-amanitin -aaaqaaqaqwaqrqatwqa -abeta -abeta42 -abietic acid -ace2 receptor -acetyl -acryl -activator/regulator complexin -acyclic 2,3-oxidosqualene -aggax -alcohols -alkaloid -alkaloids -alkylsilane -alkylsilanes -am0627 -amide -amide-i -amino acid -amphetamine -amphiliphic ligands -amphiphilic np -amphiphilic nps -amps -angiotensin converting enzyme ii -angiotensin-converting enzyme 2 -anionic ubisemiquinone -anions -anti-sars-cov-2 -antibody fab -apo -apo rnases h -aptamer -arylindenols -arylurea derivatives -aspartic acid -at-rich -atad2 -atmospheric aerosols -axggax -az -az-ab42 -azobioisostere-abeta42 -aβ peptides -b-dna -b-raf inhibitors -bacterioferritin -basket-type dna g-quadruplex -basket-type g-quadruplex -baz2b -bcl-2 -benzodiazepinedione -benzylisoquinoline -berberine -beta lactamases -bisphosphates -bn -bo3 -borohydride -boron nitride nanotube -bptu -bpy1,4-tfsi-li -brd4 bd2 -bruton's tyrosine kinase -bt4244 -c-h -c16 -ca2+ -ca2+-sensor synaptotagmin -calix-[4]-arenes -calix[n]arenes -carbapenemase -carbon nanomaterial -carbon nanomaterials -carbon nanotube -carbonic anhydrase ii -carbonyl oxygen -carboxynaphthalene disulfide -cardiac troponin c -cardiac troponin protein -carotene -cathepsins -cations -cd-nbd -cdcl3 -cefoperazone -cellobiose -cephalosporin -ceramides -cetyl -cetyltrimethylammonium bromide -cftr -cg protein -cha -chabazite -chignolin -chl1 -chloride -chloride counterions -chloroform -chlorophyll a -cholesterols -cholesteryl hemisuccinate -choline chloride -chromophores -chromosomes -clathrate hydrate -clathrate hydrates -clr -collagen -connexin 46 -connexin 50 -connexin-46/50 -coo -cooh -copper -counter ions -covid2 spike -covid2spike -crac channel -crac ion channel -crd -crebbp -ctn -cu(111) -cul3 -cullin3 -curcumin azobioisostere -cyanine -cyclophane -cyclophanes -cysteine -cyt -cyt bo3 -cytochrome -cytochrome bo3 -d alpo- -d-c14-pc -d-c18-pc -d33e -dabrafenib -dec -destabilized superoxide dismutase 1 -di-, tri- and tetra-ubiquitin -di-ubiquitin -diacylglycerol -dianions -diethyl carbonate -dihydrate -dimeric main protease -dimethoxyethane -dimethyl carbonate -dimethyl sulfoxide -dimethylallyl disphosphate -dioctylfluorene -dipeptides -diphenyl diselenide -diphenyl diselenides -diphenyl ditellurides -dmf5 -dna g-quadruplex -dna gqs -dodecyl -dope -doxyl-labeled lipids -dppe -dpsm -dsdna -dtab -e-chalcone 48 -e. coli cytoplasm -e. coli ompf -e. coli ribonuclease h -e1 -e3 ligase -ec48 -ed alpo -elusive arenium -epoxy resin -ethanolamine -ethene -ethyl -ethylene carbonate -etidocaine -exendin-4 -exendin-p5 -fa2b -factor h -falcipain-2 -falcipain‑2 -fau -faujasite -fe -fe(ii) -fluorenylmethoxycarbonyl -fluorine -fluoroalkylsilane -fluorophore -fmoc-dipeptide -folic -folinic -formate -fused in sarcoma -g protein -g1-pamam -gag-hiv1 -gc-analogue sequence -gd -gdp-mannose -gelsolin -geraa -gerac -germinant receptors -gl1 -glp-1 -glu-35 -glu-37 -glucagon-like peptide receptor -glucocorticoids -glucosidases -glycerol trihexanoate -glycine -glycogen accumulation regulator -glycopeptides -glycosyl -gold nanoparticles -gpcrs -gpis -graphene oxides -gs protein -gtpase -gva -h-ras -h2s -h3k27ac -h3k9ac -hbgt1 -heat shock protein 90 -heat-shock protein 90 -heme -heme b -hexane -hexokinase-ii -hexylthiophene -histone -hiv-1 gag protein -hkii -hno2 -hp2y1r -human and e. coli thioredoxins -human cathepsins -human formin-binding protein 21 -human lysozyme -human smoothened -human thioredoxin -hydrogenated alcohols -hydronium -hydroperoxides -hydroxyl -hydroxymethyl -igg4 antibody -imidazole -indenols -isopentenyl diphosphate -isophthalate -k+ channel -kelch-like ech-associated protein-1 -ketones -l-valine -l1 -leubethanol -li-/na-ion -lipid acyl -lipoarabinomannan -lipoglycans -lipomannan -liposome -lnx2 -locked nucleic acid -lys-353 -lysines -m -malarial m1 aminopeptidase -maldh -maltopentose -mapk -mart1 -mart1 peptide-major histocompatibility complexes -mdia2 -mek -melatonin receptors -meoh -metal ion -metal ions -methicillin-resistant staphylococcus aureus -methionine -methotrexate -methylene -methyltriphenylphosphonium bromide -micelle -micelles -mif -mif180 -mil-101 -mitochondrial anion channel -mln-4760 -monoethanolamine -monoethanolamines -morphine -mrnas -mrs2268 -mrs2500 -myeloid differentiation protein 2 -n,n-dimethylformamide -n-alkane -n-doxyl -n-doxyl pc -n-hexadecane -n-isopropylacrylamide -n-methyl-d-aspartate receptors -n-octacosane -n-octane -n-pentadecane -na2s2o3 -nag26 -nanocellulose -nanodiamond -ncps -nf-e2-related factor 2 -nf-κb -nh2 -nitric acid -nitrogen -nmda receptor -nmdars -non-structural (ns) proteins 1 -norcoclaurine synthases -ns1 -ns1 zikv -ns2b-ns3 -ns2b-ns3 dengue protease -ns2b-ns3pro -nutlin-2 -nwasp -octyl trimethyl ammonium bromide -oleoyl -oligopeptide -oligophenylenes -oligophenylenevinylene -oligosaccharide -oligothiophene -ompf -oncoprotein -organic semiconductors -otab -ox2 receptor -oxa-48 β‑lactamases -oxidosqualene cyclase -oxygen -oxyntomodulin -p-loop ntpases -p-polyphenylene -p01116-2 -p0dtc2 -p2y1 receptor -pa phosphate -palmitoyl-coa. -palmitoyl-coenzyme a -papain -para-sulfonated calix-[4]-arenes -para-sulfonato-calix[4]arenes -pc phosphate -pc-lipid -pd2l4 -pentameric formyl thiophene acetic acid -pentane -pep-synthase -peptide-major histocompatibility complex -peptidyl -peroxidase -peroxynitrite -phas -phenylalanine -pheophytin -phosophoinositides -phosphates -phosphatidyl-myo-inositol -phosphatidyl-myo-inositol mannosyltransferase a -phosphatidylcholines -phosphatidylglycerol -phosphatidylinositol bisphosphate -phosphatidylinositol hexamannosides -phosphatidylinositol phosphate -phosphocholine -phosphoenolpyruvate synthase -phosphoinositol -pim6 -pims -plasmodial cysteine protease -plasmodium falciparum m1 alanyl aminopeptidase -plastoquinol -plastoquinone -plx7904 -pmhcs -pnipaam-co-acrylamide -pnipaam-co-dimethylacrylamide -poly(n‑isopropylacrylamide) -poly-phosphatidylinositols -polycyclic aromatic hydrocarbons -polyethylene -polyproline ii -polypropylene -polysaccharide -polystyrene sulfonate -popc acyl -precambrian enzymes -precambrian proteins -pristine carbon -pristine graphene -propane -propanol -propylene carbonate -proteasome -protegens -protein kinase b -protein kinase c -protein kinase g -proteinogenic amino acids -proteolysis-targeting chimera -proteolysis-targeting chimeras -pseudoisocyanine -pyrimidine -quaterphenylene dicarbonitriles -raf -receptor binding domain -rhamnose -rna -rnase -rnase h -s protein -s1 nuclease -saccharides -sars-cov-2 dimeric main protease -sars-cov-2 main protease -sars-cov-2 mpro -sars-cov-2 rbd -sars-cov-2 receptor-binding domain -sars-cov2 spike -sclareol -sd1 -sd2 -sdpc -se -selenium -sept6 -serine -serine β-lactamases -serine/threonine kinases -siap -sio2 -sirtuin 2 -sn-2 stearoyl -snares -socony mobil-5 -sodium dodecyl sulfate -sodium thiosulfate -soluble n-ethylmaleimide sensitive factor attachment protein receptor -sopc -sorafenib -spike -staphylococcal nuclease -sterol -sucrose -sugar monomers -t4 lysozyme -tba15 -te -ted alp -tellurium diphenyl dichalcogenides -terephthalate -terpenoid -tetra-alanine -tetrabutylammonium halides -tetracyclic lanosterol -tetrahydrofuran -tetrahydroisoquinoline -tetramethylammonium -tetrapyrrole chromophore -tfncs -thalictrum flavum ncs -thiqs -threonine -tk-hefu -toll-like receptor 4 -trans-4-hydroxy-l-proline -transmembrane domains -trehalose -triglyceride -tripeptides -tropocollagen -trypsin inhibitor -tryptathionine -ubiquinol -ubiquitin conjugating enzymes -ubiquitin e3 ligase -ubiquitin ligase -urea -uridine -vascular endothelial growth factor -vemurafenib -walp23 -x-linked agammaglobulinemia -zirconium nitrate -zn -zwitterionic -α-methyl -α-methyl-substituted aldehydes -α‑amylase -β-cyclodextrin -β-lactam -β-lactamase -β-lactamases -β‑cyclodextrin diff --git a/data/SOFTNAME.txt b/data/SOFTNAME.txt deleted file mode 100644 index 08c1107..0000000 --- a/data/SOFTNAME.txt +++ /dev/null @@ -1,78 +0,0 @@ -gromacs -lammps -amber -vmd -charmm-gui -lassohtp -probis -charmming -espresso++ -plumed -ambertools -openmm -avis -cgenff-webserver -mdanalysis -modeller -moe -namd -packmol -ani2x -colabfold -cp2k -haddock -amber12 -antechamber -ase -autodock -bumpy -colvars -desmond -gaussian -gaussian09 -gromacs-ls -leap -mypresto -nemo -omegagene -pydock -swiss-model -torchani -acemd -aimd -alphafold -charmm -chimerax -colvar -cpptraj -deepmd-kit -doglycans -flexaid -gaussian16 -gibbs -glide -glycam -glycam-web -hoomd-blue -insane -intermol -jupyter -lipidbook -maestro -molpro -molsim -namd2 -optim -paramchem -pathsample -pmemd -prmtop -prody -propka -pyinteraph -pymol -rdock -rosetta -swissprot -unitymol -visual molecular dynamics diff --git a/data/SOFTVERS.txt b/data/SOFTVERS.txt deleted file mode 100644 index c7c06c1..0000000 --- a/data/SOFTVERS.txt +++ /dev/null @@ -1,59 +0,0 @@ -4.5 -3.x -4.0.7 -16 -5.0.3 -5.1.4 -2020 -5.0.3 -(v. 2016.4) -16 -19 -2016.1 -2018.3 -2020.4 -21 -4 -5.1.1 -7 -(dec 2018, stable) -(v. 2020) -(v4.6) -1.9.3 -16 rev b01 -2.0 -2.11b2 -2.6.3 -2016.2-dev-20170105-4feb0be -2016.3 -2018 -2018.0 -2018.1 -2019 -2019.3 -2020.6 -2021 -2021.4 -2023.2 -3.3.3 -4.5.6 -4.6 -4.6.5 -5.0.6 -5.0.x -5.1 -5.1.5 -5.1.x -molecular dynamics -simulation engine v5 -simulation engine version 2019.4 -software (v. 2016.4) -stable dec. 2018 -tools -v.4.6.5 -v2.4 -v4.6 -v9.16 -version 2.6.0 -version 4.6 -vina diff --git a/data/STIME.txt b/data/STIME.txt deleted file mode 100644 index 040874f..0000000 --- a/data/STIME.txt +++ /dev/null @@ -1,88 +0,0 @@ -500ns -100ns -200 ns -microsecond -500 ns -1 microsecond -200ns -100 ns -110 ns -1500 ns -300 ns -315.0 k -1 μs -10 microseconds -1000 ns -2 microseconds -250 ns -3.0 us -317.0 k -319.1 k -5 μs -50 ns -550ns -700ns -us -0.5 μs -0.6 -0.633 us -0.79 ns -1 ns -1 µs -1.0 us -1.5 micro-sec -1.6 ns -1.6 μs -10 ns -10 µs -10 μs -100 -100 nanoseconds -100–200 ns -1069 ns -1080 ns -11 ns -12 μs -120ns -125 ns -167ns -190 ns -20 -20 ns -200-300ns -27ns -298k -3 μs -30 ns -300 k -300 nanoseconds -3000 ns -300ns -316.7 k -318.4 k -320.1 k -321.8 k -36 μs -360 ns -370 ns -380 ns -4.1 us -40 ns -40ns -4200 ns -450 ns -5-microsecond -500 ps -5000 ns -500ps -50ns -600 ns -60ns -6microseconds -6µs -8 microseconds -800 ns -87 microsecond -multi-microsecond -nanosecond -one hundred nanosecond diff --git a/data/TEMP.txt b/data/TEMP.txt deleted file mode 100644 index 0e6896b..0000000 --- a/data/TEMP.txt +++ /dev/null @@ -1,53 +0,0 @@ -310k -310 k -300 k -298 k -323 k -325k -288k -303k -323k -358k -298.15 -313 k -283.15 -298.15 k -318.15 k -320k -338k -363 k -130 k -200 -210 k -225 k -242 -2500 -273.15 k -285 -293 k -293.15 -295 k -298 -298k -3000 -300k -303 -310 -313.15 k -315 -315k -316.7 k -318.4 k -320 -321.8 k -322k -333 k -338 k -340 k -3500 -360 k -363k -365 k -4000 k -473.15 k -5000 k diff --git a/data/zenodo_1009027.json b/data/zenodo_1009027.json deleted file mode 100644 index 5184ba5..0000000 --- a/data/zenodo_1009027.json +++ /dev/null @@ -1,284 +0,0 @@ -{ - "classes": [ - "SOFTNAME", - "SOFTVERS", - "STIME", - "MOL", - "FFM", - "TEMP" - ], - "raw_text": "Simulations DPPC bilayers (512 lipids) using charmm36 ff in gromacs\nCollection simulations of DPPC (512 lipids) bilayers in gromacs using the charmm36 force field.  Several temperatures between 315 and 338 K are included. The list of systems can be found below where the several parameter are:\n1) DPPC_512_NaCl_150mM_315K_v-rescale (500ns)\n2) DPPC_512_NaCl_150mM_320K (700ns)\n3) DPPC_512_NaCl_150mM_320K_v-rescale (500ns)\n4) DPPC_512_NaCl_150mM_322K_v-rescale (700ns)\n5) DPPC_512_NaCl_150mM_325K (500ns)\n6) DPPC_512_NaCl_150mM_325K_v-rescale (500ns)\n7) DPPC_512_NaCl_150mM_325K_cutoff09 (500ns)\n8) DPPC_512_NaCl_150mM_325K_MEMB_338K (500ns)\n9) DPPC_512_NaCl_150mM_338K (500ns)\nFor further information read the Readme file provided for each simulation.", - "entities": [ - { - "label": "MOL", - "text": "DPPC", - "start": 12, - "end": 16 - }, - { - "label": "FFM", - "text": "charmm36", - "start": 45, - "end": 53 - }, - { - "label": "SOFTNAME", - "text": "gromacs", - "start": 60, - "end": 67 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 94, - "end": 98 - }, - { - "label": "SOFTNAME", - "text": "gromacs", - "start": 124, - "end": 131 - }, - { - "label": "FFM", - "text": "charmm36", - "start": 142, - "end": 150 - }, - { - "label": "TEMP", - "text": "315", - "start": 194, - "end": 197 - }, - { - "label": "TEMP", - "text": "338 K", - "start": 202, - "end": 207 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 297, - "end": 301 - }, - { - "label": "MOL", - "text": "NaCl", - "start": 306, - "end": 310 - }, - { - "label": "TEMP", - "text": "315K", - "start": 317, - "end": 321 - }, - { - "label": "STIME", - "text": "500ns", - "start": 333, - "end": 338 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 343, - "end": 347 - }, - { - "label": "MOL", - "text": "NaCl", - "start": 352, - "end": 356 - }, - { - "label": "TEMP", - "text": "320K", - "start": 363, - "end": 367 - }, - { - "label": "STIME", - "text": "700ns", - "start": 369, - "end": 374 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 379, - "end": 383 - }, - { - "label": "MOL", - "text": "NaCl", - "start": 388, - "end": 392 - }, - { - "label": "TEMP", - "text": "320K", - "start": 399, - "end": 403 - }, - { - "label": "STIME", - "text": "500ns", - "start": 415, - "end": 420 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 425, - "end": 429 - }, - { - "label": "MOL", - "text": "NaCl", - "start": 434, - "end": 438 - }, - { - "label": "TEMP", - "text": "322K", - "start": 445, - "end": 449 - }, - { - "label": "STIME", - "text": "700ns", - "start": 461, - "end": 466 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 471, - "end": 475 - }, - { - "label": "MOL", - "text": "NaCl", - "start": 480, - "end": 484 - }, - { - "label": "TEMP", - "text": "325K", - "start": 491, - "end": 495 - }, - { - "label": "STIME", - "text": "500ns", - "start": 497, - "end": 502 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 507, - "end": 511 - }, - { - "label": "MOL", - "text": "NaCl", - "start": 516, - "end": 520 - }, - { - "label": "TEMP", - "text": "325K", - "start": 527, - "end": 531 - }, - { - "label": "STIME", - "text": "500ns", - "start": 543, - "end": 548 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 553, - "end": 557 - }, - { - "label": "MOL", - "text": "NaCl", - "start": 562, - "end": 566 - }, - { - "label": "TEMP", - "text": "325K", - "start": 573, - "end": 577 - }, - { - "label": "STIME", - "text": "500ns", - "start": 588, - "end": 593 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 598, - "end": 602 - }, - { - "label": "MOL", - "text": "NaCl", - "start": 607, - "end": 611 - }, - { - "label": "TEMP", - "text": "325K", - "start": 618, - "end": 622 - }, - { - "label": "TEMP", - "text": "338K", - "start": 628, - "end": 632 - }, - { - "label": "STIME", - "text": "500ns", - "start": 634, - "end": 639 - }, - { - "label": "MOL", - "text": "DPPC", - "start": 644, - "end": 648 - }, - { - "label": "MOL", - "text": "NaCl", - "start": 653, - "end": 657 - }, - { - "label": "TEMP", - "text": "338K", - "start": 664, - "end": 668 - }, - { - "label": "STIME", - "text": "500ns", - "start": 670, - "end": 675 - } - ], - "url": "https://zenodo.org/records/1009027" -} diff --git a/data/zenodo_34415.json b/data/zenodo_34415.json deleted file mode 100644 index 03be519..0000000 --- a/data/zenodo_34415.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "classes": [ - "SOFTNAME", - "SOFTVERS", - "STIME", - "MOL", - "FFM", - "TEMP" - ], - "raw_text": "POPC_AMBER_LIPID14_CaCl2_035Mol\nMD simulation trajectory and related files for fully hydrated POPC bilayer with 0.35M CaCl2. The LIPID14 force field was used with Gromacs 5.0.3. Ions were described by AMBER99SB-ILDN force field. Conditions: T=298.15, 128 POPC molecules, 6400 tip3p waters (lipid/water 1:50), 35 Ca, 70 Cl. 200ns trajectory  (preceded by 5ns NPT equillibration) (2 files of 100ns).\nTHE TRAJECTORY \"035M_CaCl2_POPC_AMB_100_200ns.xtc\" IS CORRUPTED. FOR THE UNCORRUPTED FILE PLEASE FOLLOW THE LINK: https://zenodo.org/record/46234\nThis data is ran for the nmrlipids.blospot.fi project. More details from nmrlipids.blospot.fi and https://github.com/NMRLipids/nmrlipids.blogspot.fi", - "entities": [ - { - "label": "MOL", - "text": "POPC", - "start": 94, - "end": 98 - }, - { - "label": "MOL", - "text": "CaCl2", - "start": 118, - "end": 123 - }, - { - "label": "FFM", - "text": "LIPID14", - "start": 129, - "end": 136 - }, - { - "label": "SOFTNAME", - "text": "Gromacs", - "start": 163, - "end": 170 - }, - { - "label": "SOFTVERS", - "text": "5.0.3.", - "start": 171, - "end": 177 - }, - { - "label": "FFM", - "text": "AMBER99SB-ILDN", - "start": 201, - "end": 215 - }, - { - "label": "TEMP", - "text": "298.15", - "start": 243, - "end": 249 - }, - { - "label": "MOL", - "text": "POPC", - "start": 255, - "end": 259 - }, - { - "label": "FFM", - "text": "tip3p", - "start": 276, - "end": 281 - }, - { - "label": "MOL", - "text": "Ca", - "start": 312, - "end": 314 - }, - { - "label": "MOL", - "text": "Cl", - "start": 319, - "end": 321 - }, - { - "label": "STIME", - "text": "200ns", - "start": 323, - "end": 328 - }, - { - "label": "STIME", - "text": "100ns", - "start": 390, - "end": 395 - } - ], - "url": "https://zenodo.org/records/34415" -} From 5ca47c5bad19b04e4f808ee152f6e3b64c1be75c Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 11:49:06 +0200 Subject: [PATCH 02/26] fix: update gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 00efe34..f5acb03 100644 --- a/.gitignore +++ b/.gitignore @@ -215,4 +215,6 @@ __marimo__/ *.json *.owl *.html -#!results/ground_mol_chebi.tsv + +# Results folder +!results/ From ac35c442ab6959036a9c95fed52621d9983840de Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 11:56:33 +0200 Subject: [PATCH 03/26] feat: add notebook to compute llm errors stats --- notebooks/llm_errors_count.ipynb | 266 +++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 notebooks/llm_errors_count.ipynb diff --git a/notebooks/llm_errors_count.ipynb b/notebooks/llm_errors_count.ipynb new file mode 100644 index 0000000..8c14b63 --- /dev/null +++ b/notebooks/llm_errors_count.ipynb @@ -0,0 +1,266 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "668c2949", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "cbec16d2", + "metadata": {}, + "outputs": [ + { + "ename": "FileNotFoundError", + "evalue": "[Errno 2] No such file or directory: '../llmerr.tsv'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mFileNotFoundError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m llm_error = \u001b[43mpd\u001b[49m\u001b[43m.\u001b[49m\u001b[43mread_csv\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2\u001b[39m \u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m../llmerr.tsv\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msep\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[38;5;130;43;01m\\t\u001b[39;49;00m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mheader\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnames\u001b[49m\u001b[43m=\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmodel\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43minput\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mresult\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\n\u001b[32m 3\u001b[39m \u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:873\u001b[39m, in \u001b[36mread_csv\u001b[39m\u001b[34m(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, skip_blank_lines, parse_dates, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, low_memory, memory_map, float_precision, storage_options, dtype_backend)\u001b[39m\n\u001b[32m 861\u001b[39m kwds_defaults = _refine_defaults_read(\n\u001b[32m 862\u001b[39m dialect,\n\u001b[32m 863\u001b[39m delimiter,\n\u001b[32m (...)\u001b[39m\u001b[32m 869\u001b[39m dtype_backend=dtype_backend,\n\u001b[32m 870\u001b[39m )\n\u001b[32m 871\u001b[39m kwds.update(kwds_defaults)\n\u001b[32m--> \u001b[39m\u001b[32m873\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilepath_or_buffer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:300\u001b[39m, in \u001b[36m_read\u001b[39m\u001b[34m(filepath_or_buffer, kwds)\u001b[39m\n\u001b[32m 297\u001b[39m _validate_names(kwds.get(\u001b[33m\"\u001b[39m\u001b[33mnames\u001b[39m\u001b[33m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m))\n\u001b[32m 299\u001b[39m \u001b[38;5;66;03m# Create the parser.\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m300\u001b[39m parser = \u001b[43mTextFileReader\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilepath_or_buffer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 302\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m chunksize \u001b[38;5;129;01mor\u001b[39;00m iterator:\n\u001b[32m 303\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m parser\n", + "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:1645\u001b[39m, in \u001b[36mTextFileReader.__init__\u001b[39m\u001b[34m(self, f, engine, **kwds)\u001b[39m\n\u001b[32m 1642\u001b[39m \u001b[38;5;28mself\u001b[39m.options[\u001b[33m\"\u001b[39m\u001b[33mhas_index_names\u001b[39m\u001b[33m\"\u001b[39m] = kwds[\u001b[33m\"\u001b[39m\u001b[33mhas_index_names\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m 1644\u001b[39m \u001b[38;5;28mself\u001b[39m.handles: IOHandles | \u001b[38;5;28;01mNone\u001b[39;00m = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1645\u001b[39m \u001b[38;5;28mself\u001b[39m._engine = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_make_engine\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mengine\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:1904\u001b[39m, in \u001b[36mTextFileReader._make_engine\u001b[39m\u001b[34m(self, f, engine)\u001b[39m\n\u001b[32m 1902\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m mode:\n\u001b[32m 1903\u001b[39m mode += \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m-> \u001b[39m\u001b[32m1904\u001b[39m \u001b[38;5;28mself\u001b[39m.handles = \u001b[43mget_handle\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1905\u001b[39m \u001b[43m \u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1906\u001b[39m \u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1907\u001b[39m \u001b[43m \u001b[49m\u001b[43mencoding\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mencoding\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1908\u001b[39m \u001b[43m \u001b[49m\u001b[43mcompression\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mcompression\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1909\u001b[39m \u001b[43m \u001b[49m\u001b[43mmemory_map\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmemory_map\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1910\u001b[39m \u001b[43m \u001b[49m\u001b[43mis_text\u001b[49m\u001b[43m=\u001b[49m\u001b[43mis_text\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1911\u001b[39m \u001b[43m \u001b[49m\u001b[43merrors\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mencoding_errors\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mstrict\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1912\u001b[39m \u001b[43m \u001b[49m\u001b[43mstorage_options\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mstorage_options\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1913\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1914\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mself\u001b[39m.handles \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 1915\u001b[39m f = \u001b[38;5;28mself\u001b[39m.handles.handle\n", + "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/common.py:926\u001b[39m, in \u001b[36mget_handle\u001b[39m\u001b[34m(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)\u001b[39m\n\u001b[32m 921\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(handle, \u001b[38;5;28mstr\u001b[39m):\n\u001b[32m 922\u001b[39m \u001b[38;5;66;03m# Check whether the filename is to be opened in binary mode.\u001b[39;00m\n\u001b[32m 923\u001b[39m \u001b[38;5;66;03m# Binary mode does not support 'encoding' and 'newline'.\u001b[39;00m\n\u001b[32m 924\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m ioargs.encoding \u001b[38;5;129;01mand\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m ioargs.mode:\n\u001b[32m 925\u001b[39m \u001b[38;5;66;03m# Encoding\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m926\u001b[39m handle = \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\n\u001b[32m 927\u001b[39m \u001b[43m \u001b[49m\u001b[43mhandle\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 928\u001b[39m \u001b[43m \u001b[49m\u001b[43mioargs\u001b[49m\u001b[43m.\u001b[49m\u001b[43mmode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 929\u001b[39m \u001b[43m \u001b[49m\u001b[43mencoding\u001b[49m\u001b[43m=\u001b[49m\u001b[43mioargs\u001b[49m\u001b[43m.\u001b[49m\u001b[43mencoding\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 930\u001b[39m \u001b[43m \u001b[49m\u001b[43merrors\u001b[49m\u001b[43m=\u001b[49m\u001b[43merrors\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 931\u001b[39m \u001b[43m \u001b[49m\u001b[43mnewline\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 932\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 933\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 934\u001b[39m \u001b[38;5;66;03m# Binary mode\u001b[39;00m\n\u001b[32m 935\u001b[39m handle = \u001b[38;5;28mopen\u001b[39m(handle, ioargs.mode)\n", + "\u001b[31mFileNotFoundError\u001b[39m: [Errno 2] No such file or directory: '../llmerr.tsv'" + ] + } + ], + "source": [ + "llm_error = pd.read_csv(\n", + " \"../llmerr.tsv\", sep=\"\\t\", header=None, names=[\"model\", \"input\", \"result\"]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb00ef28", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d13bb92", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error.groupby([\"model\", \"input\", \"result\"]).size()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6adef006", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b72a207c", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count = pd.DataFrame(llm_error.value_counts())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2145107d", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count = llm_error_count.reset_index()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01a9dcba", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "779008a1", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count[\"total\"] = llm_error_count.groupby(\"model\")[\"count\"].transform(\"sum\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4043b8d7", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0c460304", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count.sort_values(by=[\"model\", \"input\", \"result\"], inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "121dea2f", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0d32c81", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count = llm_error_count.loc[:, [\"total\", \"count\", \"model\", \"input\", \"result\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "358cbf58", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count = llm_error_count.sort_values([\"total\", \"count\"], ascending=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12e7fa43", + "metadata": {}, + "outputs": [], + "source": [ + "llm_error_count.to_csv(\"output.tsv\", sep=\"\\t\", index=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22623200", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "\n", + "def visualize_simulation_times(json_file):\n", + " with open(json_file) as f:\n", + " data = json.load(f)\n", + "\n", + " before_values = []\n", + " after_values = []\n", + "\n", + " for entry in data[\"normalisation_output\"]:\n", + " outputs = entry[\"output\"]\n", + " seen_in_entry = []\n", + " for result in outputs:\n", + " value = result[\"value\"]\n", + " unit = result[\"unit\"]\n", + "\n", + " if value is None:\n", + " continue\n", + " if unit == \"ps\":\n", + " value = value / 1000\n", + " elif unit == \"μs\":\n", + " value = value * 1000\n", + " elif unit == \"s\":\n", + " value = value * 1000000000\n", + "\n", + " before_values.append(value)\n", + "\n", + " if value not in seen_in_entry:\n", + " seen_in_entry.append(value)\n", + " after_values.append(value)\n", + "\n", + " all_values = before_values + after_values\n", + " min_val = min(all_values)\n", + " max_val = max(all_values)\n", + " bins = np.logspace(np.log10(min_val), np.log10(max_val), 30)\n", + "\n", + " plt.figure(figsize=(14, 5))\n", + " plt.hist(before_values, bins=bins, alpha=0.8, label=\"Before Normalisation\")\n", + " plt.hist(after_values, bins=bins, alpha=0.8, label=\"After Normalisation\")\n", + "\n", + " plt.xscale(\"log\")\n", + " plt.title(\"Simulation Time Normalisation Effect\")\n", + " plt.xlabel(\"Simulation Time (ns, log scale)\")\n", + " plt.ylabel(\"Number of entries\")\n", + " plt.legend()\n", + " plt.tight_layout()\n", + " plt.savefig(\"results/simulation_time_distribution.png\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "faf142d9", + "metadata": {}, + "outputs": [], + "source": [ + "visualize_simulation_times(\n", + " \"results/norm_simu_times/normalized_simulation_time_deepseek.json\"\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "mdverse_entity_norm (3.12.3)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 7718cf8c970bb732308d610f8ac2e8487e42f18a Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 11:56:59 +0200 Subject: [PATCH 04/26] docs: change docstrings --- .../scripts/create_mol_knowledge_graph.py | 54 +------------------ 1 file changed, 1 insertion(+), 53 deletions(-) diff --git a/src/mdverse_entity_norm/scripts/create_mol_knowledge_graph.py b/src/mdverse_entity_norm/scripts/create_mol_knowledge_graph.py index d2f9676..3648400 100644 --- a/src/mdverse_entity_norm/scripts/create_mol_knowledge_graph.py +++ b/src/mdverse_entity_norm/scripts/create_mol_knowledge_graph.py @@ -17,10 +17,6 @@ def get_molecules( ) -> list[str]: """Return the list of molecule names that were successfully normalized. - A molecule is considered normalized if it meets one of the following criteria: - - In the ChEBI and PubChem file: Match == True - - In the PDB file: the molecule has a non-null ID - Parameters ---------- chebi_file: @@ -60,10 +56,6 @@ def get_molecules( def get_json_files(entities_file: str, normalized_molecules: list[str]) -> list[str]: """Return the list of JSON files that contain at least one normalized molecule. - Reads the entities TSV and filters for MOL-category rows whose entity name - matches one of the normalized molecules. Returns the unique JSON file names - associated with those rows. - Parameters ---------- entities_file: @@ -93,10 +85,6 @@ def create_molecule_dataset_relationships( ) -> dict[str, list[str]]: """Return a mapping from each JSON dataset file to its normalized molecules. - For each JSON file that contains at least one normalized molecule, the - dictionary maps the file name to the list of normalized molecule names - found in it. - Parameters ---------- entities_file: @@ -139,10 +127,6 @@ def get_normalized_molecule_ids( ) -> list[dict]: """Return a list of records with each normalized molecule name and its database ID. - For ChEBI and PubChem, only rows where Match == True are included. - For PDB, only rows with a non-null ID are included. - Each record contains the source database, the original molecule name, and the ID. - Parameters ---------- chebi_file: @@ -210,10 +194,6 @@ def create_mol_normalisation_relationships( ) -> dict[str, str]: """Return a mapping from raw molecule names to their normalized database IDs. - Only molecules with Match == True (ChEBI, PubChem) or a non-null ID (PDB) - are included. The normalized ID is formatted as ":" for clarity - (e.g., "CHEBI:15422", "PubChem:5793", "PDB:4HKR"). - Parameters ---------- chebi_file: @@ -250,19 +230,6 @@ def create_knowledge_graph( ) -> nx.Graph: """Build and return a NetworkX knowledge graph for molecule grounding. - The graph always contains two layers of nodes and edges: - - **Dataset nodes** (gold): one node per JSON file that contains at least - one successfully normalised molecule. - - **Raw molecule nodes** (blue): one node per molecule name as it appears - in the entities TSV. - - **Dataset → raw molecule edges**: drawn from create_molecule_dataset_relationships(). - - When ``normalized`` is True, a third layer is added: - - **Normalised ID nodes** (green): one node per unique normalised ID string - (e.g. ``"CHEBI:45296"``), labelled with that ID. - - **Raw molecule → normalised ID edges**: drawn from - create_mol_normalisation_relationships(). - Parameters ---------- entities_file: @@ -326,15 +293,6 @@ def create_knowledge_graph( def visualize_knowledge_graph(graph: nx.Graph, output_path: str) -> None: """Render the knowledge graph as an interactive HTML file using PyVis. - Node colours follow the convention set in create_knowledge_graph(): - - Gold (#FFD700): dataset (JSON file) nodes — displayed larger. - - Blue (#4DA6FF): raw molecule name nodes. - - Green (#90EE90): normalised ID nodes (only present when normalized=True). - - All edges are drawn in gold with a uniform width. The physics layout uses - repulsion to spread nodes apart with low central gravity, mimicking the - style of the original graph. - Parameters ---------- graph: @@ -382,17 +340,7 @@ def visualize_knowledge_graph(graph: nx.Graph, output_path: str) -> None: def main() -> None: - """Run the full molecule grounding knowledge graph pipeline. - - Steps: - 1. Get all successfully normalised molecule names. - 2. Get the JSON dataset files that contain those molecules. - 3. Build the dataset → molecule relationship dictionary. - 4. Get the normalised molecule IDs (ChEBI / PubChem / PDB). - 5. Build the raw molecule → normalised ID relationship dictionary. - 6. Build and export the raw knowledge graph (no normalised IDs). - 7. Build and export the normalised knowledge graph (with normalised IDs). - """ + """Run the full molecule grounding knowledge graph pipeline.""" chebi_file = "results/ground_molecule/same_grounding_mol/chebi_comparaison.tsv" pubchem_file = "results/ground_molecule/same_grounding_mol/pubchem_comparaison_no_chebi_match.tsv" pdb_file = "results/ground_molecule/same_grounding_mol/pdb_uniprot_seq_entities.tsv" From 0bdb734086a1e01dafd58141c17c39650f141d8e Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 11:59:04 +0200 Subject: [PATCH 05/26] fix: update gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f5acb03..0cd3d8b 100644 --- a/.gitignore +++ b/.gitignore @@ -217,4 +217,4 @@ __marimo__/ *.html # Results folder -!results/ +!results From 017d2030d67a24f371d7328419f53d316244889e Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 12:10:45 +0200 Subject: [PATCH 06/26] fix: update git ignore --- .gitignore | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 0cd3d8b..9ebda89 100644 --- a/.gitignore +++ b/.gitignore @@ -211,10 +211,8 @@ __marimo__/ *.tsv *.parquet *.csv -*.png -*.json -*.owl -*.html # Results folder -!results +results/* +!results/FFM_normalized +!results/SOFTNAME_normalized From 343c3a4666f0b633502de0cc8b256f4e53e849a4 Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 12:11:52 +0200 Subject: [PATCH 07/26] feat: move ffm and softname normalization from data to results --- {data => results/FFM_normalized}/FFM_ground.json | 0 .../software => results/SOFTNAME_normalized}/amber/codemeta.json | 0 .../SOFTNAME_normalized}/charmm-gui/codemeta.json | 0 .../SOFTNAME_normalized}/espresso++/codemeta.json | 0 .../SOFTNAME_normalized}/gromacs/codemeta.json | 0 .../software => results/SOFTNAME_normalized}/lammps/codemeta.json | 0 .../SOFTNAME_normalized}/lassohtp/codemeta.json | 0 .../software => results/SOFTNAME_normalized}/plumed/codemeta.json | 0 .../software => results/SOFTNAME_normalized}/probis/codemeta.json | 0 {data/software => results/SOFTNAME_normalized}/vmd/codemeta.json | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename {data => results/FFM_normalized}/FFM_ground.json (100%) rename {data/software => results/SOFTNAME_normalized}/amber/codemeta.json (100%) rename {data/software => results/SOFTNAME_normalized}/charmm-gui/codemeta.json (100%) rename {data/software => results/SOFTNAME_normalized}/espresso++/codemeta.json (100%) rename {data/software => results/SOFTNAME_normalized}/gromacs/codemeta.json (100%) rename {data/software => results/SOFTNAME_normalized}/lammps/codemeta.json (100%) rename {data/software => results/SOFTNAME_normalized}/lassohtp/codemeta.json (100%) rename {data/software => results/SOFTNAME_normalized}/plumed/codemeta.json (100%) rename {data/software => results/SOFTNAME_normalized}/probis/codemeta.json (100%) rename {data/software => results/SOFTNAME_normalized}/vmd/codemeta.json (100%) diff --git a/data/FFM_ground.json b/results/FFM_normalized/FFM_ground.json similarity index 100% rename from data/FFM_ground.json rename to results/FFM_normalized/FFM_ground.json diff --git a/data/software/amber/codemeta.json b/results/SOFTNAME_normalized/amber/codemeta.json similarity index 100% rename from data/software/amber/codemeta.json rename to results/SOFTNAME_normalized/amber/codemeta.json diff --git a/data/software/charmm-gui/codemeta.json b/results/SOFTNAME_normalized/charmm-gui/codemeta.json similarity index 100% rename from data/software/charmm-gui/codemeta.json rename to results/SOFTNAME_normalized/charmm-gui/codemeta.json diff --git a/data/software/espresso++/codemeta.json b/results/SOFTNAME_normalized/espresso++/codemeta.json similarity index 100% rename from data/software/espresso++/codemeta.json rename to results/SOFTNAME_normalized/espresso++/codemeta.json diff --git a/data/software/gromacs/codemeta.json b/results/SOFTNAME_normalized/gromacs/codemeta.json similarity index 100% rename from data/software/gromacs/codemeta.json rename to results/SOFTNAME_normalized/gromacs/codemeta.json diff --git a/data/software/lammps/codemeta.json b/results/SOFTNAME_normalized/lammps/codemeta.json similarity index 100% rename from data/software/lammps/codemeta.json rename to results/SOFTNAME_normalized/lammps/codemeta.json diff --git a/data/software/lassohtp/codemeta.json b/results/SOFTNAME_normalized/lassohtp/codemeta.json similarity index 100% rename from data/software/lassohtp/codemeta.json rename to results/SOFTNAME_normalized/lassohtp/codemeta.json diff --git a/data/software/plumed/codemeta.json b/results/SOFTNAME_normalized/plumed/codemeta.json similarity index 100% rename from data/software/plumed/codemeta.json rename to results/SOFTNAME_normalized/plumed/codemeta.json diff --git a/data/software/probis/codemeta.json b/results/SOFTNAME_normalized/probis/codemeta.json similarity index 100% rename from data/software/probis/codemeta.json rename to results/SOFTNAME_normalized/probis/codemeta.json diff --git a/data/software/vmd/codemeta.json b/results/SOFTNAME_normalized/vmd/codemeta.json similarity index 100% rename from data/software/vmd/codemeta.json rename to results/SOFTNAME_normalized/vmd/codemeta.json From 3880ccbbe0fa06d720a9efcbd9ed1362f1c78251 Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 12:53:52 +0200 Subject: [PATCH 08/26] chore: move datasets for `STEMP` and `STIME` to `data/ground truth`. --- data/figshare_8046437.json | 74 ------------------- .../STEMP.json} | 0 .../STIME.json} | 0 3 files changed, 74 deletions(-) delete mode 100644 data/figshare_8046437.json rename data/{TEMP_ground_truth.json => groundtruth/STEMP.json} (100%) rename data/{STIME_ground_truth.json => groundtruth/STIME.json} (100%) diff --git a/data/figshare_8046437.json b/data/figshare_8046437.json deleted file mode 100644 index 7c3b02b..0000000 --- a/data/figshare_8046437.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "classes": [ - "SOFTNAME", - "SOFTVERS", - "STIME", - "MOL", - "FFM", - "TEMP" - ], - "raw_text": "Short molecular dynamics of a peptide inside a pure DMPC membrane\n1 ns of molecular dynamics simulation of a 19-residue peptide inside a pure DMPC membrane, performed at the NPT ensemble - 1 atm at 310K using Berendsen (semi-isotropic) and V-rescale for pressure and temperature coupling, respectively, with 1.6 ps and 0.1ps as coupling constants. Van der Waals interactions were treated using the Verlet algorithm with a 1 nm cutoff. Electrostatics where treated with particle-mesh Ewald, with a 1 nm cutoff for the real space calculations. Both the peptide and DMPC were parameterized using the GROMOS 54A7, while SPC was used for water. LINCS restraints were used on all the bonds. Peptide sequence is AAAQAAQAQWAQRQATWQA. This sequence was not taken from any real world examples, as this is a test system created for MDAnalysis datasets. The peptide was set to have be α-helical secondary structure, and was inserted manually in the membrane before minimization and equilibration. More on MDAnalysis: https://www.mdanalysis.org/", - "entities": [ - { - "label": "MOL", - "text": "DMPC", - "start": 52, - "end": 56 - }, - { - "label": "STIME", - "text": "1 ns", - "start": 66, - "end": 70 - }, - { - "label": "MOL", - "text": "DMPC", - "start": 142, - "end": 146 - }, - { - "label": "TEMP", - "text": "310K", - "start": 198, - "end": 202 - }, - { - "label": "MOL", - "text": "DMPC", - "start": 563, - "end": 567 - }, - { - "label": "FFM", - "text": "GROMOS 54A7", - "start": 597, - "end": 608 - }, - { - "label": "FFM", - "text": "SPC", - "start": 616, - "end": 619 - }, - { - "label": "MOL", - "text": "AAAQAAQAQWAQRQATWQA", - "start": 705, - "end": 724 - }, - { - "label": "SOFTNAME", - "text": "MDAnalysis", - "start": 821, - "end": 831 - }, - { - "label": "SOFTNAME", - "text": "MDAnalysis", - "start": 993, - "end": 1003 - } - ], - "url": "https://figshare.com/articles/dataset/Short_molecular_dynamics_of_a_peptide_inside_a_pure_DMPC_membrane/8046437" -} diff --git a/data/TEMP_ground_truth.json b/data/groundtruth/STEMP.json similarity index 100% rename from data/TEMP_ground_truth.json rename to data/groundtruth/STEMP.json diff --git a/data/STIME_ground_truth.json b/data/groundtruth/STIME.json similarity index 100% rename from data/STIME_ground_truth.json rename to data/groundtruth/STIME.json From d976d43d4cf6470cf47266f2c47dd7d55e2340a8 Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 12:54:26 +0200 Subject: [PATCH 09/26] chore: remove outdated `docs/` folder. --- docs/ffm.yaml | 35 ----------------------------------- docs/normalization_rules.md | 32 -------------------------------- docs/softname.yaml | 18 ------------------ 3 files changed, 85 deletions(-) delete mode 100644 docs/ffm.yaml delete mode 100644 docs/normalization_rules.md delete mode 100644 docs/softname.yaml diff --git a/docs/ffm.yaml b/docs/ffm.yaml deleted file mode 100644 index 60ab64c..0000000 --- a/docs/ffm.yaml +++ /dev/null @@ -1,35 +0,0 @@ -title: Force field and model - -FFM: - - AMBER - - CHARMM - - GROMOS - - OPLS - - MARTINI - - SPC - - TIP3P - - TIP4P - - TIP5P - - LIPID - - SLIPID - - PARM - - GAFF - - OPC - - BERGER - - SPC/E - - TRAPPE - - GAL - - GAFFLIPID - - 43A1-S3 - - AMOEBA - - HIPPO - - WILLIAMS - - SIRAH - - RSFF - - ECC-LIPIDS - - COMPASS - - L-OPLS - - Q4MD-CD - - GDML - - BIND3P - - PLUM diff --git a/docs/normalization_rules.md b/docs/normalization_rules.md deleted file mode 100644 index 428191b..0000000 --- a/docs/normalization_rules.md +++ /dev/null @@ -1,32 +0,0 @@ -# Normalization rules - -This document provides the guidelines for automatically normalizing entities retrieved from scientific texts related to molecular simulations. - -See the [annotation rules](annotation_rules.md) document for the definition of entities. - -## For all entities - -- Normalize text to lowercase (e.g., `POPC` → `popc`). To be confirmed !! - -## Molecule (MOL) - -## Force field and model (FFM) - -## Software name (SOFTNAME) - -## Software version (SOFTVERS) - -- Must contain at least one digit to be considered valid. - -## Simulation time (STIME) - -- Acceptable input units: `s`, `sec`, `second`, `seconds`, `ms`, `millisecond`, `microsec`, `microsecond`, `microseconds`, `ns`, `nanosecond`, `nanoseconds`, `ps`, `picosecond`, `picoseconds`. - -### Examples - -- `10 to 50 ns` → not `10-50 ns` - -## Simulation temperature (TEMP) - -- Convert all temperatures to Kelvin if standardization is required (`25 °C` → `298K`) -- Convert `room temperature` to `300 K` diff --git a/docs/softname.yaml b/docs/softname.yaml deleted file mode 100644 index 3b5f520..0000000 --- a/docs/softname.yaml +++ /dev/null @@ -1,18 +0,0 @@ -title: Software name - -SOFTNAME: - - amber - - autodock - - charmm - - colvar - - desmond - - gromacs - - lammps - - namd - - openbabel - - orcaflex - - plumed - - pymol - - qchem - - schrodinger - - vmd From 33f416a4771173f58c356abfc28ec78035da678c Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 12:54:56 +0200 Subject: [PATCH 10/26] chore: clean and update notebooks --- notebooks/llm_errors_count.ipynb | 266 ------------------ notebooks/score_distribution.ipynb | 154 ---------- ...emperature.ipynb => visualize_STEMP.ipynb} | 68 ++--- ..._time_simu.ipynb => visualize_STIME.ipynb} | 117 +------- 4 files changed, 33 insertions(+), 572 deletions(-) delete mode 100644 notebooks/llm_errors_count.ipynb delete mode 100644 notebooks/score_distribution.ipynb rename notebooks/{visualize temperature.ipynb => visualize_STEMP.ipynb} (54%) rename notebooks/{visualize_time_simu.ipynb => visualize_STIME.ipynb} (87%) diff --git a/notebooks/llm_errors_count.ipynb b/notebooks/llm_errors_count.ipynb deleted file mode 100644 index 8c14b63..0000000 --- a/notebooks/llm_errors_count.ipynb +++ /dev/null @@ -1,266 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "668c2949", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "cbec16d2", - "metadata": {}, - "outputs": [ - { - "ename": "FileNotFoundError", - "evalue": "[Errno 2] No such file or directory: '../llmerr.tsv'", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mFileNotFoundError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m llm_error = \u001b[43mpd\u001b[49m\u001b[43m.\u001b[49m\u001b[43mread_csv\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2\u001b[39m \u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m../llmerr.tsv\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msep\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[38;5;130;43;01m\\t\u001b[39;49;00m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mheader\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnames\u001b[49m\u001b[43m=\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmodel\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43minput\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mresult\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\n\u001b[32m 3\u001b[39m \u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:873\u001b[39m, in \u001b[36mread_csv\u001b[39m\u001b[34m(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, skip_blank_lines, parse_dates, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, low_memory, memory_map, float_precision, storage_options, dtype_backend)\u001b[39m\n\u001b[32m 861\u001b[39m kwds_defaults = _refine_defaults_read(\n\u001b[32m 862\u001b[39m dialect,\n\u001b[32m 863\u001b[39m delimiter,\n\u001b[32m (...)\u001b[39m\u001b[32m 869\u001b[39m dtype_backend=dtype_backend,\n\u001b[32m 870\u001b[39m )\n\u001b[32m 871\u001b[39m kwds.update(kwds_defaults)\n\u001b[32m--> \u001b[39m\u001b[32m873\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilepath_or_buffer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:300\u001b[39m, in \u001b[36m_read\u001b[39m\u001b[34m(filepath_or_buffer, kwds)\u001b[39m\n\u001b[32m 297\u001b[39m _validate_names(kwds.get(\u001b[33m\"\u001b[39m\u001b[33mnames\u001b[39m\u001b[33m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m))\n\u001b[32m 299\u001b[39m \u001b[38;5;66;03m# Create the parser.\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m300\u001b[39m parser = \u001b[43mTextFileReader\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilepath_or_buffer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 302\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m chunksize \u001b[38;5;129;01mor\u001b[39;00m iterator:\n\u001b[32m 303\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m parser\n", - "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:1645\u001b[39m, in \u001b[36mTextFileReader.__init__\u001b[39m\u001b[34m(self, f, engine, **kwds)\u001b[39m\n\u001b[32m 1642\u001b[39m \u001b[38;5;28mself\u001b[39m.options[\u001b[33m\"\u001b[39m\u001b[33mhas_index_names\u001b[39m\u001b[33m\"\u001b[39m] = kwds[\u001b[33m\"\u001b[39m\u001b[33mhas_index_names\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m 1644\u001b[39m \u001b[38;5;28mself\u001b[39m.handles: IOHandles | \u001b[38;5;28;01mNone\u001b[39;00m = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1645\u001b[39m \u001b[38;5;28mself\u001b[39m._engine = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_make_engine\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mengine\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:1904\u001b[39m, in \u001b[36mTextFileReader._make_engine\u001b[39m\u001b[34m(self, f, engine)\u001b[39m\n\u001b[32m 1902\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m mode:\n\u001b[32m 1903\u001b[39m mode += \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m-> \u001b[39m\u001b[32m1904\u001b[39m \u001b[38;5;28mself\u001b[39m.handles = \u001b[43mget_handle\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1905\u001b[39m \u001b[43m \u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1906\u001b[39m \u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1907\u001b[39m \u001b[43m \u001b[49m\u001b[43mencoding\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mencoding\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1908\u001b[39m \u001b[43m \u001b[49m\u001b[43mcompression\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mcompression\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1909\u001b[39m \u001b[43m \u001b[49m\u001b[43mmemory_map\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmemory_map\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1910\u001b[39m \u001b[43m \u001b[49m\u001b[43mis_text\u001b[49m\u001b[43m=\u001b[49m\u001b[43mis_text\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1911\u001b[39m \u001b[43m \u001b[49m\u001b[43merrors\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mencoding_errors\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mstrict\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1912\u001b[39m \u001b[43m \u001b[49m\u001b[43mstorage_options\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mstorage_options\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1913\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1914\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mself\u001b[39m.handles \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 1915\u001b[39m f = \u001b[38;5;28mself\u001b[39m.handles.handle\n", - "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/common.py:926\u001b[39m, in \u001b[36mget_handle\u001b[39m\u001b[34m(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)\u001b[39m\n\u001b[32m 921\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(handle, \u001b[38;5;28mstr\u001b[39m):\n\u001b[32m 922\u001b[39m \u001b[38;5;66;03m# Check whether the filename is to be opened in binary mode.\u001b[39;00m\n\u001b[32m 923\u001b[39m \u001b[38;5;66;03m# Binary mode does not support 'encoding' and 'newline'.\u001b[39;00m\n\u001b[32m 924\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m ioargs.encoding \u001b[38;5;129;01mand\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m ioargs.mode:\n\u001b[32m 925\u001b[39m \u001b[38;5;66;03m# Encoding\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m926\u001b[39m handle = \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\n\u001b[32m 927\u001b[39m \u001b[43m \u001b[49m\u001b[43mhandle\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 928\u001b[39m \u001b[43m \u001b[49m\u001b[43mioargs\u001b[49m\u001b[43m.\u001b[49m\u001b[43mmode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 929\u001b[39m \u001b[43m \u001b[49m\u001b[43mencoding\u001b[49m\u001b[43m=\u001b[49m\u001b[43mioargs\u001b[49m\u001b[43m.\u001b[49m\u001b[43mencoding\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 930\u001b[39m \u001b[43m \u001b[49m\u001b[43merrors\u001b[49m\u001b[43m=\u001b[49m\u001b[43merrors\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 931\u001b[39m \u001b[43m \u001b[49m\u001b[43mnewline\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 932\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 933\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 934\u001b[39m \u001b[38;5;66;03m# Binary mode\u001b[39;00m\n\u001b[32m 935\u001b[39m handle = \u001b[38;5;28mopen\u001b[39m(handle, ioargs.mode)\n", - "\u001b[31mFileNotFoundError\u001b[39m: [Errno 2] No such file or directory: '../llmerr.tsv'" - ] - } - ], - "source": [ - "llm_error = pd.read_csv(\n", - " \"../llmerr.tsv\", sep=\"\\t\", header=None, names=[\"model\", \"input\", \"result\"]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb00ef28", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8d13bb92", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error.groupby([\"model\", \"input\", \"result\"]).size()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6adef006", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b72a207c", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count = pd.DataFrame(llm_error.value_counts())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2145107d", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count = llm_error_count.reset_index()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "01a9dcba", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "779008a1", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count[\"total\"] = llm_error_count.groupby(\"model\")[\"count\"].transform(\"sum\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4043b8d7", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0c460304", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count.sort_values(by=[\"model\", \"input\", \"result\"], inplace=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "121dea2f", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0d32c81", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count = llm_error_count.loc[:, [\"total\", \"count\", \"model\", \"input\", \"result\"]]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "358cbf58", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count = llm_error_count.sort_values([\"total\", \"count\"], ascending=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12e7fa43", - "metadata": {}, - "outputs": [], - "source": [ - "llm_error_count.to_csv(\"output.tsv\", sep=\"\\t\", index=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "22623200", - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "\n", - "def visualize_simulation_times(json_file):\n", - " with open(json_file) as f:\n", - " data = json.load(f)\n", - "\n", - " before_values = []\n", - " after_values = []\n", - "\n", - " for entry in data[\"normalisation_output\"]:\n", - " outputs = entry[\"output\"]\n", - " seen_in_entry = []\n", - " for result in outputs:\n", - " value = result[\"value\"]\n", - " unit = result[\"unit\"]\n", - "\n", - " if value is None:\n", - " continue\n", - " if unit == \"ps\":\n", - " value = value / 1000\n", - " elif unit == \"μs\":\n", - " value = value * 1000\n", - " elif unit == \"s\":\n", - " value = value * 1000000000\n", - "\n", - " before_values.append(value)\n", - "\n", - " if value not in seen_in_entry:\n", - " seen_in_entry.append(value)\n", - " after_values.append(value)\n", - "\n", - " all_values = before_values + after_values\n", - " min_val = min(all_values)\n", - " max_val = max(all_values)\n", - " bins = np.logspace(np.log10(min_val), np.log10(max_val), 30)\n", - "\n", - " plt.figure(figsize=(14, 5))\n", - " plt.hist(before_values, bins=bins, alpha=0.8, label=\"Before Normalisation\")\n", - " plt.hist(after_values, bins=bins, alpha=0.8, label=\"After Normalisation\")\n", - "\n", - " plt.xscale(\"log\")\n", - " plt.title(\"Simulation Time Normalisation Effect\")\n", - " plt.xlabel(\"Simulation Time (ns, log scale)\")\n", - " plt.ylabel(\"Number of entries\")\n", - " plt.legend()\n", - " plt.tight_layout()\n", - " plt.savefig(\"results/simulation_time_distribution.png\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "faf142d9", - "metadata": {}, - "outputs": [], - "source": [ - "visualize_simulation_times(\n", - " \"results/norm_simu_times/normalized_simulation_time_deepseek.json\"\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "mdverse_entity_norm (3.12.3)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/score_distribution.ipynb b/notebooks/score_distribution.ipynb deleted file mode 100644 index d83ca46..0000000 --- a/notebooks/score_distribution.ipynb +++ /dev/null @@ -1,154 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 5, - "id": "a42be492", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The autoreload extension is already loaded. To reload it, use:\n", - " %reload_ext autoreload\n" - ] - } - ], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "8d607635", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import pandas as pd\n" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "94a15a3f", - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(\"../results/grounded_molecules.tsv\", sep=\"\\t\")\n", - "\n", - "df = df[df[\"MOL_SCORE\"] != \"Not Available\"]\n", - "dfchebi = df[df[\"MOL_TYPE\"] == \"CHEBI\"]\n", - "scores = pd.to_numeric(dfchebi[\"MOL_SCORE\"])\n" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "2bc8a689", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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9uxBo0aNCk323bFjh97C0cspz9QM29qGGDoGERFJgM6FjJ2dHXr16lUeWYiIiIh0onMhs3r16vLIQaQmV+Wj9sPbAIA4F28DpyEiImNWpptGEpUns7xcfPntBAC8RQEREZVM50LGy8urxOvF/P777y8UiIiIiEhbOhcy48aN03idm5uLy5cvY//+/Zg8ebK+chERERGVSudCZuzYsUW2L1++HBcuXHjhQERERETa0tstrIODg7F9+3Z9rY6IiIioVHorZLZt2wZ7e3t9rY6IiIioVDoPLTVr1kxjsq8QAomJiXj06BG+/vprvYYjIiIiKonOhUyPHj00XsvlclSvXh0dOnRA/fr19ZWLXmL5chOs69Bf/ZyIiKg4OhcyM2bMKI8cRGp5pmZY33GAoWMQEZEE6G2ODBEREVFF0/qIjFwuL/FCeAAgk8mQl5f3wqHo5SZTqeD++D4A4F41NwOnISIiY6Z1IbNz585il8XExGDZsmVQqVR6CUUvN0VeDv7vq3AAvEUBERGVTOtCpnv37oXabty4galTp2L37t0YMGAAZs+erddwRERERCUp0xyZhIQEDBs2DE2aNEFeXh5iY2OxZs0aeHh46DsfERERUbF0KmRSU1MxZcoU1K5dG1evXsWRI0ewe/duNG7cuLzyERERERVL66GlBQsW4LPPPoOzszM2btxY5FATUXmoaqVEvkrARF7yZPMCuvQlIiJp07qQmTp1KiwsLFC7dm2sWbMGa9asKbLfjh079BaOCACszU1hIpchcudl3H+cXmJft2rWmNqzWQUlIyIiQ9O6kBk4cGCpp18Tlaf7j9MRl5hm6BhERGREtC5koqKiyjEG0d/y5SbY2qaX+jkREVFxdL5FAVF5yzM1w3+Chhg6BhERSQBvUUBERESSxSMyZHRkKhUcUx8BAJJtqxs4DRERGTMWMmR0FHk5WLskDABvUUBERCXj0BIRERFJFgsZIiIikiwWMkRERCRZLGSIiIhIsljIEBERkWSxkCEiIiLJ4unXZHRUchP82LKr+jkREVFxWMi8hPJVAiZy7W4Aqktffck1NcPyNz+okPcy9t+FPnIYS24iovLAQuYlZCKXIXLnZdx/nF5iP7dq1pjas1kFpTIMKf4utM0MGFduIqLywELmJXX/cTriEtMMHaNoQsA283m2VEubcn87o/5dFEOKmYmIygML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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.hist(scores, bins=40, color=\"steelblue\", edgecolor=\"white\")\n", - "\n", - "# Adding a vertical line at the mean score value\n", - "plt.axvline(\n", - " scores.mean(), color=\"red\", linestyle=\"--\", label=f\"Mean = {scores.mean():.3f}\"\n", - ")\n", - "\n", - "plt.title(\"Distribution of Scores for Chebi\")\n", - "plt.xlabel(\"Score\")\n", - "plt.ylabel(\"Number of entries\")\n", - "plt.legend()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "1f36dbca", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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ycklHjc8PBw2Njbi4urqqsar0D35snxNh0BERKQyWv0Yy9/fX1z38vJCq1at4Obmhi1btsDMzKzMxw0LC8OECRPE7bS0NCY8rzA0MMTAnwYiJinmjXX9G/pj/v/mV0BUREREZaPVyc7rbG1tUbt2bcTGxqJLly7IycnBs2fP5Fp3UlJSiuzj8yqpVAqpVKrmaHVbTFIMriRceWO9uk5131iHdJChIdCnz8t1IiIdptWPsV6Xnp6OuLg4ODs7o3nz5jA2NsbRo0fF/Tdu3EBCQgK8vb01GCWRHjA1BbZuLVhMTTUdDRFRuWh1y87EiRPRo0cPuLm5ITExETNnzoShoSEGDBgAGxsbjBgxAhMmTICdnR2sra3x6aefwtvbmyOxiIiISKTVyc69e/cwYMAAPH78GPb29mjTpg3+/PNP2NvbAwCWLVsGAwMDBAQEIDs7G35+fvj22281HDURERFpE61OdjZv3lziflNTU6xatQqrVq2qoIiI3hIZGQVTRQBAejpgYaHZeIiIykGn+uwQERERKYvJDhEREek1JjtERESk15jsEBERkV5jskNERER6jckOERER6TWtHnpORBpiaAh06/ZynYhIhzHZISJFpqbA3r2ajoKISCX4GIuIiIj0GpMdIiIi0mtMdohIUUZGwRQRFhYF60REOox9doioaJmZmo6AiEgl2LJDREREeo3JDhEREek1JjtERESk15jsEBERkV5jskNERER6jaOxiEiRgQHg6/tynYhIhzHZISJFZmZAZKSmoyAiUgn+yUZERER6jckOERER6TUmO0SkKCMDsLcvWDhdBBHpOPbZIaKiPXqk6QiIiFSCLTtERESk15jsEBERkV5jskNERER6jckOERER6TUmO0RERKTXOBqLiBQZGAAtWrxcJyLSYUx2iEiRmRlw4YKmoyAiUgn+yUZERER6jckOERER6TUmO0SkKDMTcHcvWDIzNR0NEVG5sM+OmuXL8mFoYKjyukRqJQjAnTsv14mIdJhWJzvh4eHYsWMH/v33X5iZmaF169b48ssvUadOHbFO+/btceLECbnPjRo1CqtXr67ocItkaGCIgT8NRExSTIn16jnXw8aRGysoKiIioreHVic7J06cQHBwMN555x3k5eVh2rRpeO+993D9+nVYWFiI9QIDAzFnzhxx29zcXBPhFismKQZXEq5oOgwi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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Plot the score distribution as a histogram using 40 bins (from the Gilda results)\n", - "plt.hist(scores2, bins=40, color=\"darkgreen\", edgecolor=\"white\")\n", - "\n", - "# Adding a vertical line at the mean score value\n", - "plt.axvline(\n", - " scores2.mean(), color=\"red\", linestyle=\"--\", label=f\"Mean = {scores2.mean():.3f}\"\n", - ")\n", - "\n", - "# Set the title and labels for the plot\n", - "plt.title(\"Distribution of Scores for Gilda\")\n", - "plt.xlabel(\"Score\")\n", - "plt.ylabel(\"Number of entries\")\n", - "plt.legend()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "mdverse_entity_norm (3.12.3)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/visualize temperature.ipynb b/notebooks/visualize_STEMP.ipynb similarity index 54% rename from notebooks/visualize temperature.ipynb rename to notebooks/visualize_STEMP.ipynb index 757bcf7..b485f0e 100644 --- a/notebooks/visualize temperature.ipynb +++ b/notebooks/visualize_STEMP.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "235b3f20", "metadata": {}, "outputs": [], @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "c9cfd882", "metadata": {}, "outputs": [ @@ -37,12 +37,9 @@ "start = int(df[\"normalised_temperature\"].min())\n", "end = int(df[\"normalised_temperature\"].max()) + 100\n", "plt.xticks(np.arange(0, end, 400))\n", - "\n", "plt.title(\"Distribution of Normalised Temperatures\", fontsize=16)\n", "plt.xlabel(\"Temperature\", fontsize=14)\n", "plt.ylabel(\"Frequency\", fontsize=14)\n", - "\n", - "\n", "plt.show()" ] }, @@ -51,11 +48,26 @@ "execution_count": null, "id": "e6221d02", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OK\n" + ] + }, + { + "data": { + "image/png": "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JiwszDRq1MhkZmY69E2cODHX/f+ZJGOz2cymTZsc2tu0aWMKFSrkcOzr169vPD09zc6dO+1tWVlZ5uGHHzaSzKpVq+zt135O3bp1y7bPa8egbdu2Dsd0ypQpRpIJDAw0x44ds7df+x159NFHHcZJSUnJNvbu3buNt7e36dOnj0N7kyZNTJkyZRzartV4I9OnTzeSzPPPP+/wni9dupRt23fffddIMosWLbK3Xfu9HzlyZI7jSzJxcXH257/88oux2WymQYMGJj093d7+22+/meDgYFOmTBlz5coVh9db/fkBrsSlOiAP3n//fQUGBqpjx446c+aM/XHhwgW1a9dOR44c0f79+x1e07FjR5UpU8b+vHjx4qpcubI8PDyyzf40atRImZmZOV7WevHFFx0us9SuXVv333+/Vq1aZb9EsWDBAtlsNsXFxTnUd+bMGbVv314XL17Uxo0bHcatUaOGWrZsmW1/fn5+9v1lZGTo3LlzOnPmjGJjY5WVlaUtW7Y4d/CckFNNX3/9tU6dOqWePXvqwoULDu/t2ozEypUrLY0fExOjunXrOrQ1b95cV65csR/706dP67vvvlP79u1VvXp1+3Y2m00vv/yyJOmTTz7JNvbgwYNz3W///v0dfobXPnTQvn17lSpVyt5+7Xfkz79L/v7+kv64XJycnKwzZ87Yt/3++++tvPUbSkhIUL9+/dS2bVtNnDjR3m6z2eTn5ydJunr1qv34N2/eXJJuat+fffaZjDF68cUX5e3tbW+PjIxUz549dfToUW3fvt3hNVZ+foCrcakOyIM9e/bo4sWL9ksYOTl16pQqVapkf16+fPls2xQtWlQRERHy8fHJ1i7JYd3INdcugV2vatWqWrlypY4ePap//OMf2rNnj4wxqlKlyg3ru971tV7vypUrGj9+vP73v//pwIEDMsY49J8/fz7XfdysnGras2ePJKlXr165vu7P7y03Of1MQkNDJf3fsT98+LAk6R//+Ee2baOjo+Xh4aFDhw5l68vteOa032s/72vrgv7cd/ToUYe27du3a8SIEVqzZo1SU1Md+nIawxk//fSTunbtqmrVqumDDz6Qh4fj39cfffSR3nzzTW3fvl2ZmZkOfTfzu3Cj43yt7dChQ6pTp4693crPD3A1ghOQB8YYFS9eXAsXLsx1m+vXG0nK9aPXN/pI9p9DijP12Ww2ffXVV7mO/+d/oAoXLpzjdoMGDdLbb7+trl276uWXX1aJEiXk5eWlbdu26aWXXlJWVpalmm50w8XcFnTnVNO1YzJhwgTVrFkzx9dFRkZaqik/jv01uR3PG+03t/brazl27JgaN26soKAgjRgxQpUrV5a/v79sNpsGDBhwUwujT548qbZt28rf31/Lly/PtiZp6dKl6tq1q+677z5NmTJFpUqVkq+vr65evapWrVpZ/l1wlfz8+QG5ITgBeVCxYkXt27dP9erV+8sFr662Z88e1atXz6Ft9+7d8vT0tF8KrFixohISElS6dOkcZ6ic8d5776lx48b68MMPHdoPHDiQbdsbhaOQkBBJ0rlz5xw+XZWWlqbExERVqFDBUj3XFj77+/vneGnR1a7N4Pz888/Z+n755RdlZWXlOPORXz755BOlpKTo888/V7NmzRz6zp49m2320qpLly6pXbt2OnPmjNatW6eSJUtm2+a9996Tr6+vvvnmG4dg+Msvv2Tb1tk7k187hj///HO2DxTs3r3bYRugILHGCciDJ598UllZWRo6dGiO/VYvFeXF66+/7vDX9LZt27Rq1Sq1aNHCHuK6d+8uSRo2bFi2j8s7W5+np2e2v95TU1M1adKkbNte2/+5c+ey9V27dLVq1SqH9kmTJjk1UxEbG6sSJUpo/PjxOe7n8uXL2W7lcDNKlCih+vXra9myZdq1a5e93RijcePGSZIeeughl+3vr1ybZfnzz2T27Nk6efJknsY0xuiJJ57Q1q1b9f7776t27dq57ttmszn8vIwxGjNmTLZtb/S7kJP27dvLZrNpwoQJDpcAExMTNW/ePJUpU0b33HOPM28LyBfMOAF5cO0WBFOnTtW2bdvUtm1bFStWTL/++qs2btyoAwcO5LjuxRWOHj2q2NhYtW/fXomJiZo6dar8/Pw0YcIE+zb33nuv4uPjFR8fr5o1a+rhhx9WZGSkEhMTtXXrVn355ZfKyMiw/F5nzpyprl27qmXLljp16pTmzp1rX0tyvXvvvVceHh4aO3aszp8/L39/f5UrV05169ZVy5YtVblyZb3yyis6e/asypUrp/Xr12vTpk0qVqyY5ffv7++v//3vf+rYsaMqV66sXr16qUKFCrpw4YJ++eUXLV26VJ988kmOd6vOqylTpqhJkyZq1KiR/XYEy5cv14oVK/T444+rRYsWLtvXX2ndurUKFy6s7t2767nnnlPRokW1YcMGffnll4qKirJ8H6vrzZgxQ5988omaNGmi1NRUvf/++w799evXV/ny5dWlSxctWbJEzZs315NPPqnMzEx9+umnunTpUrYxQ0NDVaFCBX344YeKioqy38/q+ntxXa9y5cr697//rddff12NGzdW165d7bcjSElJ0YIFC7jTONwCwQnIo7lz56pZs2aaNWuWxo0bp4yMDIWHh6tWrVr2mYj8kJCQoEGDBmnkyJG6fPmy6tWrpwkTJjh84kuSRo4cqTp16uitt97S5MmTlZqaqhIlSqhatWp66623LO9v4sSJCgwM1EcffaTPPvtMpUqV0tNPP617770326Wy0qVLa+7cuXrttdfUp08fZWZmKi4uTnXr1pWnp6c+//xz9e/fX2+//ba8vb31wAMPaO3atWrQoIFTxyA2NlabN2/W+PHj9f777+v3339X0aJFFRUVpUGDBmU7FjerTp06+u677zRy5EhNnz5dqampKl++vF577TW98MILLt3XX4mKitJXX32lYcOG6dVXX5Wnp6caNGigtWvX6rnnnsvTp8muzUCuXbtWa9euzdY/b948lS9fXo8++qguXryoSZMmafDgwSpatKjatWun8ePH5xikFyxYoIEDB2rYsGG6dOmSypQpk2twkqTXXntNFSpU0PTp0zVkyBB5e3urbt26WrhwYbavOwIKis2wgg64JfTo0UP//e9/WfQKAAWINU4AAAAWEZwAAAAsIjgBAABYxBonAAAAi5hxAgAAsIjgBAAAYBHBCQAAwCKCEwAAgEUEJwAAAIsITgAAABYRnAAAACwiOAEAAFhEcAIAALCI4AQAAGARwQkAAMAighMAAIBFBCcAAACL7ojgZIxRcnKyjDEFXQoAALiF3RHB6eLFiwoODtbFixcLuhQAAHALuyOCEwAAgCsQnAAAACwiOAEAAFhEcAIAALCI4AQAAGARwQkAAMAighMAAIBFBCcAAACLCE4AAAAWEZwAAAAsIjgBAABYRHACAACwiOAEAABgEcEJAADAIoITAACARQQnAAAAiwhOAAAAFhGcAAAALHKL4PTbb7/piSeeUGhoqPz8/HT33Xdry5Yt9n5jjF555RVFRETIz89PLVu21P79+wuwYgAAcCcq8OB0/vx5NWjQQF5eXvrqq6+0e/duvfnmmypatKh9m9dff11vvfWWZsyYoe+//17+/v6KjY1VWlpaAVYOAADuNDZjjCnIAoYMGaINGzbo22+/zbHfGKPIyEi98MILGjx4sCQpKSlJYWFhmj9/vh599NG/3EdycrKCg4OVlJSkoKAgl9YPAADuHAU+4/T555+rTp06evjhh1WiRAndc889mj17tr3/8OHDOnnypFq2bGlvCw4OVt26dbVx48aCKBkAANyhCjw4HTp0SO+8844qVqyoFStWqE+fPurfv7/++9//SpJOnjwpSQoLC3N4XVhYmL3vz9LT05WcnOzwAAAAuFmFCrqArKws1alTR6+++qok6Z577tGuXbs0Y8YMxcXF5WnMcePGadSoUa4sEwAAoOBnnCIiIlS1alWHtujoaB07dkySFB4eLkk6deqUwzanTp2y9/3Z0KFDlZSUZH8cP348HyoHAAB3mgIPTg0aNNDevXsd2vbt26cyZcpIksqVK6fw8HCtXr3a3p+cnKzvv/9eMTExOY7p4+OjoKAghwcAAMDNKvBLdQMHDlT9+vX16quv6pFHHtEPP/ygWbNmadasWZIkm82mAQMGaMyYMapYsaLKlSunESNGKDIyUh07dizY4gEAwB2lwG9HIEnLly/X0KFDtX//fpUrV06DBg3SU089Ze83xmjkyJGaNWuWLly4oIYNG2r69OmqVKmSpfG5HQEAAHAFtwhO+Y3gBAAAXKHA1zgBAADcKghOAAAAFhGcAAAALCI4AQAAWERwAgAAsIjgBAAAYBHBCQAAwCKCEwAAgEUEJwAAAIsITgAAABYRnAAAACwiOAEAAFhEcAIAALCI4AQAAGARwQkAAMAighMAAIBFBCcAAACLCE4AAAAWEZwAAAAsIjjBJdasWSObzZbjY9OmTfbtXn31VdWrV0/FixeXr6+vKlasqAEDBuj3338vwOoBALCmUEEXgNtL//79de+99zq0VahQwf7fW7duVc2aNfXoo48qMDBQe/bs0ezZs/XFF19ox44d8vf3/7tLBgDAMoITXKpRo0bq0qVLrv1LlizJ1hYTE6MuXbpo2bJlevTRR/OzPAAAbgqX6uByFy9e1JUrVyxvX7ZsWUnShQsX8qcgAABchOAEl+rZs6eCgoLk6+urZs2aacuWLdm2McbozJkzOnnypL799lv1799fnp6eatq06d9fMAAATuBSHVzC29tbnTt3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ePzfJNWfA9M1f74xZkFLTk5WcHCwkpKSFBQUlC/7+P3iFc3ecCFfxsad46kGRVQ8ME/fvQ0A+Bs4fYaeO3duroEJAADgduZ0cOrRo0c+lAEAAOD+nF4cDgAAcKdyesapXLlyf3mp7tChQ3kuCAAAwF05HZw6dOiQLTidP39ea9eulTFGnTp1cllxAAAA7sTp4DR58uQc2zMyMtSxY0eVK1fuZmsCAABwSy5b4+Tt7a3nnntOEyZMcNWQAAAAbsWli8PPnDmjixcvunJIAAAAt+H0pbqlS5dma8vIyNCePXs0depUNW/e3CWFAQAAuBung1OXLl1ybPfy8lKnTp309ttv33RRAAAA7sjp4HT48OFsbb6+vipRogR3FAcAALc1p4PT0aNHVatWLQUEBGTrS01N1datW9W4cWOXFAcAAOBOnF4c3qxZM+3evTvHvl9++UXNmjW76aIAAADckdPByRiTa19qaqr8/PxuqiAAAAB3ZelS3aZNm/Tdd9/Zny9cuFDr16932CYtLU2fffaZoqOjXVshAACAm7AUnFasWKFRo0ZJkmw2m956661s23h5eSk6OlrTp093bYUAAABuwtKlupEjRyorK0tZWVkyxmjTpk3259ce6enp2rFjh+rXr5/fNQMAABQIpz9Vl5WVlR91AAAAuD2ng9M1aWlpOnTokNLS0rL11apV66aKAgAAcEdOB6eMjAz16dNH77//vq5cuZLjNlevXr3pwgAAANyN07cjGDVqlFauXKn58+fLGKOpU6dq3rx5atGihcqWLatly5blR50AAAAFzungtHjxYsXHx+uRRx6RJN1333168skntXLlSjVs2JDgBADADYwdO1Y2m03VqlVzaF+5cqV69+6tatWqydPTU2XLli2YAnFDTgenX3/9VZUqVZKnp6d8fX11/vx5e98TTzyhxYsXu7RAAABuF7/++qteffVV+fv7Z+tbuHChFi5cqODgYEVGRhZAdbDC6eAUERGhCxcuSJLKlSunNWvW2Pv27dvnqroAALjtDB48WPXq1VOdOnWy9b366qtKTk7Whg0bVKNGjQKoDlY4vTi8adOm+vbbb9WuXTs99dRTGjx4sPbs2SNvb299+umnevzxx/OjTgAAbmnr1q3Txx9/rO3bt6tfv37Z+pllujU4HZzGjh2rM2fOSJIGDBggY4w+/vhjXb58Wf3799crr7zi8iIBALiVXb16Vf369dM///lP3X333QVdDm6CU8HpypUrOnHihEqVKmVvGzhwoAYOHOjywgAAuF3MmDFDR48e1apVqwq6FNwkp9Y4eXh4qF69etq5c2d+1QMAwG3l7NmzeuWVVzRixAgVL168oMvBTXI6OJUvX97hk3QAACB3w4cPV0hISI7rmnDrcfpTdcOGDdN//vMfnThxIj/qAQDgtrF//37NmjVL/fv314kTJ3TkyBEdOXJEaWlpyszM1JEjR3Tu3LmCLhNOcHpx+OLFi/X777+rfPnyql69usLCwmSz2ez9NptNn332mUuLBADgVvTbb78pKytL/fv3V//+/bP1lytXTs8//7wmT5789xeHPHE6OKWkpKhKlSoOzwEAQHbVqlXTJ598kq19+PDhunjxoqZMmaKoqKgCqAx55XRw+uabb/KjDgAAbjvFihVTx44ds7Vfm2G6vu/HH3/U559/Lkk6cOCAkpKSNGbMGElSjRo11K5du/wuFxY4HZwAAIDrbdu2TSNGjHBou/Y8Li6O4OQmnF4cLkk///yzHn30UUVFRcnHx0fbtm2TJL388sv66quvXFogAAC3mzVr1mjXrl0ObT169JAxJsfH/PnzC6ZQZON0cPr66691zz336OjRo+rWrZsyMzPtfV5eXpo+fbpLCwQAAHAXTgenoUOH6tFHH9XGjRuzfb3KPffco+3bt7usOAAAAHfidHDatWuXunfvLkkOtyGQpCJFiti/xw4AAOB243RwCgkJyfXml/v27VNERMRNFwUAAOCOnA5OHTt21MiRI7V37157m81m08mTJ/XGG2+oc+fOLi0QAADAXTgdnMaNG6fixYurevXqqlu3riSpV69eqly5soKDgxUfH+/qGgEAANyC0/dxCg4O1nfffaf3339fX3/9tUJCQhQSEqK+ffvqySeflLe3d37UCQC4BWVmXdWp9IsFXQZucWE+gfLy8CzoMiTl8QaYXl5e6tmzp3r27OnqegAAt5FT6Rc1bNeXBV0GbnGvVmujkn5FCroMSXm8Aab0x0Lw999/XxMmTNCCBQsc1jzl1fjx42Wz2TRgwAB7W1pamvr27avQ0FAFBASoc+fOOnXq1E3vCwAAwFl5+pLfp59+Wh999JGysrLk6+urtLQ0eXh46OGHH9bs2bMVEBDgdCGbN2/WzJkzVb16dYf2gQMH6osvvtDixYsVHBys5557Tp06ddKGDRuc3gcAAMDNcHrGqV+/flq+fLlmz56tpKQkXbp0SUlJSZo1a5a++OIL9evXz+kiUlJS1K1bN82ePVtFixa1tyclJWnOnDmaOHGimjdvrtq1a2vevHn67rvvtGnTJqf3AwAAcDOcDk5LlizRa6+9pp49eyowMFCSFBgYqF69emn8+PFaunSp00X07dtXDz74oFq2bOnQvnXrVmVmZjq0V6lSRaVLl9bGjRud3g8AAMDNcPpSna+vr8qVK5djX/ny5eXl5eXUeB9++KG2bdumzZs3Z+s7efKkvL29VaRIEYf2sLAwnTx5Mtcx09PTlZ6ebn+enJzsVE0AAAA5cXrGqWfPnnrnnXdkjHFoN8Zo+vTpTn3S7vjx43r++ee1YMEC+fr6OltKrsaNG6fg4GD7o1SpUi4bGwAA3LmcnnEKCQnRtm3bVLFiRbVr104lSpTQ6dOntWzZMqWnp6thw4aaOHGipD/uKD5w4MBcx9q6datOnz6tWrVq2duuXr2qdevWaerUqVqxYoUyMjJ04cIFh1mnU6dOKTw8PNdxhw4dqkGDBtmfJycnE54AAMBNs5k/Tx39BQ8P65NUNptNV69ezbX/4sWLOnr0qENbz549VaVKFb300ksqVaqUihcvrg8++MD+VS579+5VlSpVtHHjRtWrV89SHcnJyQoODlZSUpKCgoIs1++M3y9e0ewNF/JlbNw5nmpQRMUD83R7NcAt/Xr5Avdxwk1zp/s4OX2GzsrKctnOAwMDVa1aNYc2f39/hYaG2tt79+6tQYMGKSQkREFBQerXr59iYmIshyYAAABXcfs/bSdNmiQPDw917txZ6enpio2N1fTp0wu6LAAAcAfKc3D66aefdPz4caWlpWXr69SpU54LWrNmjcNzX19fTZs2TdOmTcvzmAAAAK7gdHDatWuXHnnkEe3duzfbJ+ukv17XBAAAcKtyOjj17t1bhQoV0ueff65KlSrJ29s7P+oCAABwO04Hp59//lkff/yxWrVqlR/1AAAAuC2nb4BZs2ZNnT59Oj9qAQAAcGtOB6epU6fqzTff1Ndff60rV67kR00AAABuyelLdVWrVlW9evXUqlUreXh4yM/Pz6HfZrMpKSnJZQUCAAC4C6eD0zPPPKMPPvhAnTp1YnE4AAC4ozgdnJYsWaKJEyfq2WefzY96AAAA3JbTa5yKFCmi8uXL50ctAAAAbs3p4PTCCy/o7bffZmE4AAC44zh9qe7AgQP66aefFBUVpSZNmqhIkSIO/TabTVOmTHFVfQAAAG7D6eC0fPlyeXp6SpK+/fbbbP0EJwAAcLtyOjgdPnw4P+oAAABwe06vcQIAALhT5Sk4nTlzRkOGDFGLFi1UqVIl/fzzz5KkKVOmaNOmTS4tEAAAwF04HZy2bdumihUr6sMPP1TJkiV18OBBpaenS5J+++03TZo0yeVFAgAAuAOng9PAgQMVExOj/fv3a86cOTLG2Pvq1q3LjBMAALhtOb04fPPmzVq6dKm8vLx09epVh77ixYvr9OnTLisOAADAnTg94+Tv76/k5OQc+44dO6bQ0NCbLgoAAMAdOR2cYmNjNWbMGJ09e9beZrPZdPnyZU2ZMkVt2rRxaYEAAADuwung9Nprryk5OVkVK1bUI488IpvNpuHDh6tq1ao6e/asxowZkx91AgAAFDing9Ndd92lHTt2qF+/fkpMTFRUVJTOnj2rbt26acuWLSpRokR+1AkAAFDgnF4cLklFihTRqFGjNGrUKFfXAwAA4LacnnEqX768du7cmWPfrl27VL58+ZsuCgAAwB05HZyOHDliv+Hln126dEnHjx+/6aIAAADckaVLdWlpabp06ZL9ZpfJyck6d+5ctm0+/fRTRUZGur5KAAAAN2ApOL322msaPXq0pD9uPRAbG5vrtvHx8S4pDAAAwN1YCk4dO3ZU2bJlZYxRr169NHz4cEVFRTls4+3trejoaNWsWTM/6gQAAChwloJTjRo1VKNGDUl/zDi1bduWO4QDAIA7jtO3I4iLi8uPOgAAANye05+qAwAAuFMRnAAAACwiOAEAAFhEcAIAALAoT99VJ0l79uzRli1bdPz4cfXq1Uvh4eE6cOCAwsLCFBgY6MoaAQAA3ILTwenSpUv65z//qY8++kg2m01ZWVlq1aqVwsPDNXToUJUrV06vv/56ftQKAABQoJy+VDd48GD9v//3//Tll18qOTnZ/jUsktSmTRslJCS4tEAAAAB34fSM08cff6wJEybogQce0NWrVx36ypYtqyNHjriqNgAAALfi9IxTSkqKIiIicuxLTU296YIAAADcldPBqXr16lqyZEmOfV988YXq1Klz00UBAAC4I6cv1Y0YMUIdOnTQpUuX9PDDD8tms+mHH37QBx98oLlz5+rLL7/MjzoBAAAKnNMzTg8++KA+/PBDrV+/Xh07dpQxRs8++6wWLVqkBQsWqEWLFvlRJwAAQIHL032cunTpoi5dumjfvn06c+aMQkJCVKVKFVfXBgAA4FbyfANMSapUqZIqVarkqloAAADcmtPBafTo0Tfst9lsGjFiRJ4LAgAAcFdOB6dJkyZla0tJSdHVq1fl5+cnHx8fghMAALgtOb04/Pz589kely9f1ldffaUKFSpozZo1+VAmAABAwbupNU72QQoVUmxsrH777Tf16dNHGzZscMWwAAAAbsXpGacbKVmypHbs2OHKIQEAANyGy4LT4cOH9dprrykqKspVQwIAALgVpy/VBQYGymazObRlZmYqIyNDhQsX1tKlS11WHAAAgDtxOji98MIL2YKTr6+vSpYsqdatWyskJMRlxQEAALgTp4NTfHx8PpQBAADg/ly6OBwAAOB25nRwKleunMqXL2/pYWWh+DvvvKPq1asrKChIQUFBiomJ0VdffWXvT0tLU9++fRUaGqqAgAB17txZp06dcrZsAACAm+b0pboOHTpoyZIlSk5OVsuWLRUWFqZTp05p1apVCg4OVufOnZ0ar2TJkho/frwqVqwoY4z++9//qkOHDtq+fbv+8Y9/aODAgfriiy+0ePFiBQcH67nnnlOnTp24VxQAAPjbOR2c7rrrLpUqVUoJCQkKCgqytyclJal169YKDw/XSy+9ZHm8du3aOTwfO3as3nnnHW3atEklS5bUnDlztHDhQjVv3lySNG/ePEVHR2vTpk2qV6+es+UDAADkmdOX6t566y0NHTrUITRJUnBwsIYMGaK33347z8VcvXpVH374oVJTUxUTE6OtW7cqMzNTLVu2tG9TpUoVlS5dWhs3bszzfgAAAPLC6Rmnc+fOKSkpKce+pKQknT9/3ukifvrpJ8XExCgtLU0BAQH65JNPVLVqVe3YsUPe3t4qUqSIw/ZhYWE6efJkruOlp6crPT3d/jw5OdnpmgAAAP7M6RmnFi1a6KWXXtLatWsd2tesWaMhQ4aoRYsWThdRuXJl7dixQ99//7369OmjuLg47d692+lxrhk3bpyCg4Ptj1KlSuV5LAAAgGucDk4zZ85UZGSkmjdvrpCQEFWuXFkhISFq0aKFIiIiNGPGDKeL8Pb2VoUKFVS7dm2NGzdONWrU0JQpUxQeHq6MjAxduHDBYftTp04pPDw81/GGDh2qpKQk++P48eNO1wQAAPBnTl+qi4iI0ObNm5WQkKAffvhBiYmJioiI0H333adWrVq5pKisrCylp6erdu3a8vLy0urVq+2f1tu7d6+OHTummJiYXF/v4+MjHx8fl9QCAABwjdPB6ZpWrVq5JCgNHTpUrVu3VunSpXXx4kUtXLhQa9as0YoVKxQcHKzevXtr0KBBCgkJUVBQkPr166eYmBg+UQcAAP52loLTuXPnVKRIEXl4eOjcuXN/ub0z31d3+vRpPfnkk0pMTFRwcLCqV6+uFStW6P7775ckTZo0SR4eHurcubPS09MVGxur6dOnWx4fAADAVSwFp+LFi2vjxo267777VKxYsWxf8vtnV69etVzAnDlzbtjv6+uradOmadq0aZbHBAAAyA+WgtPcuXPtX58yd+7cvwxOAAAAtyNLwSkuLs7+3z169MivWgAAANya07cjAAAAuFM5HZwuX76sYcOGqVKlSipcuLA8PT2zPQAAAG5HTt+OoG/fvlq4cKEee+wxVa1aVd7e3vlRFwAAgNtxOjgtW7ZMb7zxhp577rn8qAcAAMBtOX2pztPTU5UqVcqPWgAAANya08GpT58+eu+99/KjFgAAALfm9KW6woUL69tvv1X9+vXVsmVLFSlSxKHfZrNp4MCBrqoPAADAbTgdnF566SVJ0rFjx7Rp06Zs/QQnAABwu3I6OGVlZeVHHQAAAG6PG2ACAABY5PSM07p16/5ym8aNG+epGAAAAHfmdHBq2rSpbDabjDH2tj9/6e/Vq1dvvjIAAAA343Rw2r59e7a28+fPa8WKFVqyZIlmzpzpksIAAADcjdPBqUaNGjm2N23aVIULF9bMmTPVrFmzmy4MAADA3bh0cXj9+vX15ZdfunJIAAAAt+HS4PTpp58qJCTElUMCAAC4Dacv1bVv3z5bW0ZGhvbu3atjx47p9ddfd0lhAAAA7sbp4JScnJztU3S+vr5q2bKlunTpotjYWJcVBwAA4E6cDk5r1qzJhzIAAADcH3cOBwAAsIjgBAAAYBHBCQAAwCKCEwAAgEWWgtNbb72l06dPS5KOHTumzMzMfC0KAADAHVkKTgMHDtTRo0clSeXKlcvx++oAAABud5aCU2hoqA4ePChJMsZku48TAADAncDSfZwefPBBPfnkkxoyZIhsNps6duwoHx+fHLe12Wz2kAUAAHA7sRScZs2apYYNG2rPnj2aOHGiGjdurPDw8PyuDQAAwK1YCk5eXl7q3bu3JGnJkiUaMmSIatSoka+FAQAAuBunv3Ll8OHD+VEHAACA23M6OEnSb7/9psmTJ2v9+vU6d+6cQkJC1KhRIz3//PO66667XF0jAACAW3D6Bpi7du3S3XffrRkzZigiIkLNmzdXRESEZsyYoerVq+vnn3/OjzoBAAAKnNMzToMHD1ZUVJRWrlypokWL2tvPnz+vBx54QIMHD9ZXX33l0iIBAADcgdMzTuvXr9fw4cMdQpMkFS1aVC+//LLWr1/vsuIAAADcidPBqVChQkpPT8+xLz09XZ6enjddFAAAgDtyOji1bNlSL7/8svbt2+fQvn//fo0YMUL333+/y4oDAABwJ04Hp4kTJ+rKlSuqWrWqatasqdjYWN1zzz2Kjo7WlStXNHHixPyoEwAAoMA5HZxKly6tn376SRMnTlSlSpWUlZWlSpUqadKkSfrxxx9VqlSp/KgTAACgwOXpPk4BAQHq37+/+vfv7+p6AAAA3JbTM04AAAB3KoITAACARQQnAAAAiwhOAAAAFjkVnNLS0jRx4kTt2rUrv+oBAABwW04FJ19fXw0fPlxnz57Nr3oAAADcltOX6mrWrKndu3fnRy0AAABuzen7OE2ZMkXdunVT8eLF1aZNGxUuXDg/6gIAAHA7Tgen5s2bKyMjQ127dpUkFS5cWDabzd5vs9mUlJTkugoBAADchNPB6YUXXnAISgAAAHcKp4NTfHx8PpQBAADg/m7qPk7Hjx/Xd999p9TUVFfVAwAA4LbyFJxmzZqlu+66S2XKlFGjRo20d+9eSdJDDz2kKVOmuLRAAAAAd+F0cJo8ebL69eunJ598UitXrpQxxt7XtGlTLV682Knxxo0bp3vvvVeBgYEqUaKEOnbsaA9i16Slpalv374KDQ1VQECAOnfurFOnTjlbOgAAwE1xOji9/fbbGjFihMaNG6dmzZo59FWuXDlb6Pkra9euVd++fbVp0yZ9/fXXyszM1AMPPOBw+W/gwIFatmyZFi9erLVr1+rEiRPq1KmTs6UDAADcFKcXh//222+qX79+jn1eXl5KSUlxaryEhASH5/Pnz1eJEiW0detWNW7cWElJSZozZ44WLlyo5s2bS5LmzZun6Ohobdq0SfXq1XP2LQAAAOSJ0zNOZcqU0Q8//JBj3/fff69KlSrdVEHX7gEVEhIiSdq6dasyMzPVsmVL+zZVqlRR6dKltXHjxhzHSE9PV3JyssMDAADgZjkdnJ566imNGTNGc+bMsQeSzMxMffHFF5owYYKeeeaZPBeTlZWlAQMGqEGDBqpWrZok6eTJk/L29laRIkUctg0LC9PJkydzHGfcuHEKDg62P0qVKpXnmgAAAK5xOjgNHjxYvXr10tNPP63ixYtLkho0aKAOHTqoe/fuevbZZ/NcTN++fbVr1y59+OGHeR5DkoYOHaqkpCT74/jx4zc1HgAAgJSHNU6S9NZbb+n555/XqlWrdPbsWYWEhKhFixaqWLFingt57rnntHz5cq1bt04lS5a0t4eHhysjI0MXLlxwmHU6deqUwsPDcxzLx8dHPj4+ea4FAAAgJ3kKTpIUFRWlqKiomy7AGKN+/frpk08+0Zo1a1SuXDmH/tq1a8vLy0urV69W586dJUl79+7VsWPHFBMTc9P7BwAAsCpPwSkzM1Pz58/X999/r8TEREVERKhevXqKi4uTl5eXU2P17dtXCxcu1GeffabAwED7uqXg4GD5+fkpODhYvXv31qBBgxQSEqKgoCD169dPMTExfKIOAAD8rZxe47Rv3z5VrlxZffr00fbt22WM0fbt2/Wvf/1LlSpVcvo+Tu+8846SkpLUtGlTRURE2B+LFi2ybzNp0iS1bdtWnTt3VuPGjRUeHq6lS5c6WzoAAMBNcXrG6ZlnnpG3t7f27t3rcKnuwIEDateunfr06aP/9//+n+Xxrr/zeG58fX01bdo0TZs2zdlyAQAAXMbpGafvv/9eY8eOzba+qUKFCho9erQ2bdrksuIAAADcidPBKTIyUjabLcc+m82W6yfdAAAAbnVOB6eRI0dqxIgROnTokEP7oUOHNHLkSI0cOdJlxQEAALgTS2uc2rdv7/D8woULqly5sqpVq6YSJUro9OnT2rVrl8LCwrRkyRLFxcXlS7EAAAAFyVJwSk5Odrg8V6lSJft30mVkZKhIkSJq2LChJOnixYv5UCYAAEDBsxSc1qxZk89lAAAAuD+n1zgBAADcqfJ05/Djx4/r008/1fHjx5WWlubQZ7PZNGXKFJcUBwAA4E6cDk4fffSRunfvrqysLJUoUULe3t4O/QQnAABwu3I6OA0bNkwdO3bUrFmzFBwcnB81AQAAuCWn1zj9/vvvevrppwlNAADgjuN0cGrVqhVfqwIAAO5ITl+qmzFjhrp27apLly6pRYsWKlKkSLZtatWq5YraAAAA3IrTwenixYu6dOmSxo0bp/Hjxzv0GWNks9l09epVlxUIAADgLpwOTk8++aSOHTumt99+W5UqVcr2qToAAIDbldPB6YcfftDChQvVsWPHfCgHAADAfTm9OLxixYq6cuVKftQCAADg1pwOThMnTtTYsWP1yy+/5Ec9AAAAbsvpS3UDBgzQyZMnVa1aNUVGRmb7VJ3NZtPOnTtdVR8AAIDbcDo41a5dWzabLT9qAQAAcGtOB6f58+fnQxkAAADuz+k1TgAAAHcqp2ecevXq9ZfbzJ07N0/FAAAAuDOng9P27duztZ0/f17Hjx9XsWLFdNddd7mkMAAAAHfjkuAkSXv27NFjjz2mN99886aLAgAAcEcuW+MUHR2tl156SQMHDnTVkAAAAG7FpYvDg4ODdeDAAVcOCQAA4DacvlR37ty5bG0ZGRnas2ePhg0bpmrVqrmkMAAAAHfjdHAqVqxYjjfANMaoVKlS+vTTT11RFwAAgNtxOjjNnTs3W3Dy9fVVyZIlVbduXRUq5PSQAAAAtwSnU06PHj3yoQwAAAD3x53DAQAALLI041SuXDnLX+xrs9l08ODBmyoKAADAHVkKTh06dPjL4PTjjz/qm2++sRywAAAAbjWWgtPkyZNz7duxY4dGjx6tNWvWKCoqSkOHDnVVbQAAAG4lz2uctmzZovbt26t27dras2eP/vvf/2rv3r2WvgQYAADgVuR0cNq0aZNat26tunXr6siRI1q4cKF2796tJ554Qh4erDUHAAC3L8tJZ926dbr//vtVv359nT59Wh9//LF+/PFHde3alXVNAADgjmBpjVPTpk317bff6r777tPy5cvVpk2b/K4LAADA7VgKTuvWrZMk7dq1S48++ugNt7XZbEpKSrr5ygAAANyMpeA0cuTI/K4DAADA7RGcAAAALOJjcAAAABYRnAAAACwiOAEAAFhEcAIAALCI4AQAAGARwQkAAMAighMAAIBFBCcAAACLCE4AAAAWEZwAAAAsIjgBAABYRHACAACwqMCD07p169SuXTtFRkbKZrPp008/deg3xuiVV15RRESE/Pz81LJlS+3fv79gigUAAHe0Ag9OqampqlGjhqZNm5Zj/+uvv6633npLM2bM0Pfffy9/f3/FxsYqLS3tb64UAADc6QoVdAGtW7dW69atc+wzxmjy5MkaPny4OnToIEn63//+p7CwMH366ad69NFH/85SAQDAHa7AZ5xu5PDhwzp58qRatmxpbwsODlbdunW1cePGXF+Xnp6u5ORkhwcAAMDNcuvgdPLkSUlSWFiYQ3tYWJi9Lyfjxo1TcHCw/VGqVKl8rRMAANwZ3Do45dXQoUOVlJRkfxw/frygSwIAALcBtw5O4eHhkqRTp045tJ86dcrelxMfHx8FBQU5PAAAAG6WWwencuXKKTw8XKtXr7a3JScn6/vvv1dMTEwBVgYAAO5EBf6pupSUFB04cMD+/PDhw9qxY4dCQkJUunRpDRgwQGPGjFHFihVVrlw5jRgxQpGRkerYsWPBFQ0AAO5IBR6ctmzZombNmtmfDxo0SJIUFxen+fPn68UXX1RqaqqefvppXbhwQQ0bNlRCQoJ8fX0LqmQAAHCHKvDg1LRpUxljcu232WwaPXq0Ro8e/TdWBQAAkJ1br3ECAABwJwQnAAAAiwhOAAAAFhGcAAAALCI4AQAAWERwAgAAsIjgBAAAYBHBCQAAwCKCEwAAgEUEJwAAAIsITgAAABYRnAAAACwiOAEAAFhEcAIAALCI4AQAAGARwQkAAMAighMAAIBFBCcAAACLCE4AAAAWEZwAAAAsIjgBAABYRHACAACwiOAEAABgEcEJAADAIoITAACARQQnAAAAiwhOAAAAFhGcAAAALCI4AQAAWERwAgAAsIjgBAAAYBHBCQAAwCKCEwAAgEUEJwAAAIsITgAAABYRnAAAACwiOAEAAFhEcAIAALCI4AQAAGARwQkAAMAighMAAIBFBCcAAACLCE4AAAAWEZwAAAAsIjgBAABYRHACAACwiOAEAABgEcEJAADAIoITAACARQQnAAAAiwhOAAAAFhGcAAAALCI4AQAAWERwAgAAsOiWCU7Tpk1T2bJl5evrq7p16+qHH34o6JIAAMAd5pYITosWLdKgQYM0cuRIbdu2TTVq1FBsbKxOnz5d0KUBAIA7yC0RnCZOnKinnnpKPXv2VNWqVTVjxgwVLlxYc+fOLejSAADAHaRQQRfwVzIyMrR161YNHTrU3ubh4aGWLVtq48aNOb4mPT1d6enp9udJSUmSpOTk5Hyr0zPL6NG7b4kcCjfmeTVVycm2gi4DcBm/LKOhpRsWdBm4xfmlGyVn5t+/4dcEBgbKZrvxOdjtg9OZM2d09epVhYWFObSHhYXpl19+yfE148aN06hRo7K1lypVKl9qBAAAt76kpCQFBQXdcBu3D055MXToUA0aNMj+PCsrS+fOnVNoaOhfJknkj+TkZJUqVUrHjx//y19KALiTcH50H4GBgX+5jdsHp2LFisnT01OnTp1yaD916pTCw8NzfI2Pj498fHwc2ooUKZJfJcIJQUFBnBgAIAecH28Nbr8ox9vbW7Vr19bq1avtbVlZWVq9erViYmIKsDIAAHCncfsZJ0kaNGiQ4uLiVKdOHd13332aPHmyUlNT1bNnz4IuDQAA3EFuieDUtWtX/f7773rllVd08uRJ1axZUwkJCdkWjMN9+fj4aOTIkdkuoQLAnY7z463FZowxBV0EAADArcDt1zgBAAC4C4ITAACARQQnAAAAiwhObiw+Pl5hYWGy2Wz69NNPC7oct1e2bFlNnjzZ/vzvOG49evRQx44d83UfwJ3OGKOnn35aISEhstls2rFjR0GX5JauP+cdOXLkbzlWTZs21YABA/J1H+6G4ORiPXr0kM1msz9CQ0PVqlUr/fjjj06Ns2fPHo0aNUozZ85UYmKiWrdunU8V375cedxyOwlNmTJF8+fPd8k+gDvZxo0b5enpqQcffDBbX0JCgubPn6/ly5crMTFR1apV4w/Kv1CqVCn7sXKFNWvWyGaz6cKFCw7tS5cu1X/+8x+X7ONWQXDKB61atVJiYqISExO1evVqFSpUSG3btnVqjIMHD0qSOnTooPDw8Dx/TDUzMzNPr8sPGRkZf+v+bua4WRUcHMxd6QEXmDNnjvr166d169bpxIkTDn0HDx5URESE6tevr/DwcBUq5Lo76fyd58i/8xzo6enp8mOVk5CQEEtfU3I7ITjlAx8fH4WHhys8PFw1a9bUkCFDdPz4cf3+++/2bY4fP65HHnlERYoUUUhIiDp06KAjR45I+uMSXbt27SRJHh4e9u/Xy8rK0ujRo1WyZEn5+PjY72d1zbVZkUWLFqlJkyby9fXVggULJEnvvvuuoqOj5evrqypVqmj69Ok3fA9NmzZV//799eKLLyokJETh4eGKj4932ObYsWPq0KGDAgICFBQUpEceecThq3Hi4+NVs2ZNvfvuuypXrpx8fX0l/TGdPHPmTLVt21aFCxdWdHS0Nm7cqAMHDqhp06by9/dX/fr17eFR+uPE2aFDB4WFhSkgIED33nuvVq1adcP3cP1fpPHx8Q4zgdce12aLEhIS1LBhQxUpUkShoaFq27atw/7LlSsnSbrnnntks9nUtGlTSdkv1aWnp6t///4qUaKEfH191bBhQ23evNnef+2vttWrV6tOnToqXLiw6tevr717997wvQC3s5SUFC1atEh9+vTRgw8+6DCL26NHD/Xr10/Hjh2TzWZT2bJlVbZsWUnSQw89ZG+75rPPPlOtWrXk6+ur8uXLa9SoUbpy5Yq932az6Z133lH79u3l7++vsWPH5lhT2bJl9eqrr6pXr14KDAxU6dKlNWvWLIdtfvrpJzVv3lx+fn4KDQ3V008/rZSUFIfaO3bsqLFjxyoyMlKVK1e2n6c/+ugjNWrUSH5+frr33nu1b98+bd68WXXq1FFAQIBat27t8G/G5s2bdf/996tYsWIKDg5WkyZNtG3btlyP6Z9nyf98NeTaY82aNZKk9957T3Xq1FFgYKDCw8P1+OOP6/Tp0/axmjVrJkkqWrSobDabevToISn7pbrz58/rySefVNGiRVW4cGG1bt1a+/fvt/fPnz9fRYoU0YoVKxQdHa2AgAD7ZMMtw8Cl4uLiTIcOHezPL168aJ555hlToUIFc/XqVWOMMRkZGSY6Otr06tXL/Pjjj2b37t3m8ccfN5UrVzbp6enm4sWLZt68eUaSSUxMNImJicYYYyZOnGiCgoLMBx98YH755Rfz4osvGi8vL7Nv3z5jjDGHDx82kkzZsmXNkiVLzKFDh8yJEyfM+++/byIiIuxtS5YsMSEhIWb+/Pm5vo8mTZqYoKAgEx8fb/bt22f++9//GpvNZlauXGmMMebq1aumZs2apmHDhmbLli1m06ZNpnbt2qZJkyb2MUaOHGn8/f1Nq1atzLZt28zOnTuNMcZIMnfddZdZtGiR2bt3r+nYsaMpW7asad68uUlISDC7d+829erVM61atbKPtWPHDjNjxgzz008/mX379pnhw4cbX19fc/ToUfs2ZcqUMZMmTbI/l2Q++eQT+8/h2rFMTEw0b7zxhilcuLD56aefjDHGfPzxx2bJkiVm//79Zvv27aZdu3bm7rvvtv/MfvjhByPJrFq1yiQmJpqzZ8/m+PPu37+/iYyMNF9++aX5+eefTVxcnClatKh9+2+++cZIMnXr1jVr1qwxP//8s2nUqJGpX7/+jX+xgNvYnDlzTJ06dYwxxixbtsxERUWZrKwsY4wxFy5cMKNHjzYlS5Y0iYmJ5vTp0+b06dNGkpk3b569zRhj1q1bZ4KCgsz8+fPNwYMHzcqVK03ZsmVNfHy8fV+STIkSJczcuXPNwYMHHc4h1ytTpowJCQkx06ZNM/v37zfjxo0zHh4e5pdffjHGGJOSkmIiIiJMp06dzE8//WRWr15typUrZ+Li4uxjxMXFmYCAANO9e3eza9cus2vXLvt5ukqVKg7nu9q1a5umTZua9evXm23btpkKFSqYf/3rX/axVq9ebd577z2zZ88es3v3btO7d28TFhZmkpOTHd7btXPetf1s377dfhyvPwc+//zzpkSJEvZ/X+bMmWO+/PJLc/DgQbNx40YTExNjWrdubYwx5sqVK2bJkiVGktm7d69JTEw0Fy5cMMb88W/F888/b6+hffv2Jjo62qxbt87s2LHDxMbGmgoVKpiMjAxjjDHz5s0zXl5epmXLlmbz5s1m69atJjo62jz++OPWf2EKGMHJxeLi4oynp6fx9/c3/v7+RpKJiIgwW7dutW/z3nvvmcqVK9tPDMYYk56ebvz8/MyKFSuMMcZ88skn5s+5NjIy0owdO9ah7d577zXPPvusMeb//keZPHmywzZRUVFm4cKFDm3/+c9/TExMTK7vo0mTJqZhw4bZ9vXSSy8ZY4xZuXKl8fT0NMeOHbP3//zzz0aS+eGHH4wxfwQnLy8v+0ntGklm+PDh9ucbN240ksycOXPsbR988IHx9fXNtT5jjPnHP/5h3n77bfvzGwWn623cuNH4+vqaRYsW5Tr277//biTZg9WfT0LXXB+cUlJSjJeXl1mwYIG9PyMjw0RGRprXX3/dGPN/wWnVqlX2bb744gsjyVy+fPmG7xe4XdWvX99+3srMzDTFihUz33zzjb1/0qRJpkyZMg6vyen/7xYtWphXX33Voe29994zERERDq8bMGDAX9ZUpkwZ88QTT9ifZ2VlmRIlSph33nnHGGPMrFmzTNGiRU1KSop9my+++MJ4eHiYkydPGmP+OD+EhYWZ9PR0+zbXziXvvvuuve2DDz4wkszq1avtbePGjTOVK1fOtb6rV6+awMBAs2zZMof3lltwut6SJUuMr6+vWb9+fa7jb9682UgyFy9eNMb837nr/PnzDttdH5z27dtnJJkNGzbY+8+cOWP8/PzMRx99ZIwx9kmBAwcO2LeZNm2aCQsLy7UWd8OlunzQrFkz7dixQzt27NAPP/yg2NhYtW7dWkePHpUk7dy5UwcOHFBgYKACAgIUEBCgkJAQpaWlOVweul5ycrJOnDihBg0aOLQ3aNBAe/bscWirU6eO/b9TU1N18OBB9e7d276vgIAAjRkzJtd9XVO9enWH5xEREfap2z179qhUqVIqVaqUvb9q1aoqUqSIQz1lypRR8eLFbzj2ta/Oufvuux3a0tLSlJycLOmPqfzBgwcrOjpaRYoUUUBAgPbs2aNjx47d8D382bFjx9SxY0cNHjxYjzzyiL19//79euyxx1S+fHkFBQXZp/6dGf/gwYPKzMx0+Bl5eXnpvvvuy/Yzuv79R0RESJL92AJ3kr179+qHH37QY489JkkqVKiQunbtqjlz5jg91s6dOzV69GiHc91TTz2lxMREXbp0yb7d9efIG7n+/1Obzabw8HCHc2CNGjXk7+9v36ZBgwbKyspyuPR+9913y9vb+4Zj53YOvP6ccOrUKT311FOqWLGigoODFRQUpJSUFKfPgdu3b1f37t01depUh3PV1q1b1a5dO5UuXVqBgYFq0qSJJOfOgXv27FGhQoVUt25de1toaKgqV67scA4sXLiwoqKi7M+v/7flVnBLfFfdrcbf318VKlSwP3/33XcVHBys2bNna8yYMUpJSVHt2rXt64+ul1PIyMv+r7l2vX327NkOv8zSH4sHb8TLy8vhuc1mU1ZWVp5ryW3sa2u4cmq7tr/Bgwfr66+/1htvvKEKFSrIz89PXbp0cWqxZWpqqtq3b6+YmBiNHj3aoa9du3YqU6aMZs+ercjISGVlZalatWr5tpjzRu8VuJPMmTNHV65cUWRkpL3NGCMfHx9NnTpVwcHBlsdKSUnRqFGj1KlTp2x919ZYSrmfl/6soM+B1+8rLi5OZ8+e1ZQpU1SmTBn5+PgoJibGqXPUyZMn1b59e/3zn/9U79697e2pqamKjY1VbGysFixYoOLFi+vYsWOKjY3Nl3NgTsfV3ELf/kZw+hvYbDZ5eHjo8uXLkqRatWpp0aJFKlGihIKCgiyNERQUpMjISG3YsMH+l4AkbdiwQffdd1+urwsLC1NkZKQOHTqkbt263dwbuU50dLSOHz+u48eP22eddu/erQsXLqhq1aou2881GzZsUI8ePfTQQw9J+uMEeW0xvRXGGD3xxBPKysrSe++9Zz9RSdLZs2e1d+9ezZ49W40aNZIkrV+/3uH11/5ivHr1aq77iIqKkre3tzZs2KAyZcpI+uMTO5s3b77j7nMCWHHlyhX973//05tvvqkHHnjAoa9jx4764IMP9K9//SvH13p5eWX7/7FWrVrau3evwx+u+SU6Olrz589XamqqPRxt2LBBHh4eqly5ssv3t2HDBk2fPl1t2rSR9McHjM6cOWP59WlpaerQoYOqVKmiiRMnOvT98ssvOnv2rMaPH28/n2/ZssVhGyvnwOjoaF25ckXff/+96tevL+n/zq/58e9CQSE45YP09HSdPHlS0h+fMJg6dapSUlLsn5Tr1q2bJkyYoA4dOtg/JXf06FEtXbpUL774okqWLJnjuP/+9781cuRIRUVFqWbNmpo3b5527NiR48zV9UaNGqX+/fsrODhYrVq1Unp6urZs2aLz589r0KBBeXqPLVu21N13361u3bpp8uTJunLlip599lk1adLE8jS4MypWrKilS5eqXbt2stlsGjFihFN/+cXHx2vVqlVauXKlUlJS7DNxwcHBKlq0qEJDQzVr1ixFRETo2LFjGjJkiMPrS5QoIT8/PyUkJKhkyZLy9fXN9pewv7+/+vTpo3//+98KCQlR6dKl9frrr+vSpUsOf90B+MPy5ct1/vx59e7dO9v/T507d9acOXNyDU5ly5bV6tWr1aBBA/n4+Kho0aJ65ZVX1LZtW5UuXVpdunSRh4eHdu7cqV27dmnMmDEurb1bt24aOXKk4uLiFB8fr99//139+vVT9+7d7ZfeXKlixYr2T74lJyfr3//+t/z8/Cy//plnntHx48e1evVqh0/rXTtXeXt76+2339a//vUv7dq1K9u9mcqUKSObzably5erTZs28vPzU0BAQLYaO3TooKeeekozZ85UYGCghgwZorvuuksdOnS4uQPgRljjlA8SEhIUERGhiIgI1a1bV5s3b9bixYvtH2EvXLiw1q1bp9KlS6tTp06Kjo5W7969lZaWdsMZqP79+2vQoEF64YUXdPfddyshIUGff/65KlaseMN6/vnPf+rdd9/VvHnzdPfdd6tJkyaaP3++/SP2eWGz2fTZZ5+paNGiaty4sVq2bKny5ctr0aJFeR7zRiZOnKiiRYuqfv36ateunWJjY1WrVi3Lr1+7dq1SUlJUv359+88mIiJCixYtkoeHhz788ENt3bpV1apV08CBAzVhwgSH1xcqVEhvvfWWZs6cqcjIyFxPAuPHj1fnzp3VvXt31apVSwcOHNCKFStUtGjRm3r/wO1ozpw5atmyZY6X4zp37qwtW7bkevPgN998U19//bVKlSqle+65R5IUGxur5cuXa+XKlbr33ntVr149TZo0yT4D7EqFCxfWihUrdO7cOd17773q0qWLWrRooalTp7p8X9Ifx+r8+fOqVauWunfvbr/tiVVr165VYmKiqlat6nAO/O6771S8eHHNnz9fixcvVtWqVTV+/Hi98cYbDq+/6667NGrUKA0ZMkRhYWF67rnnctzPvHnzVLt2bbVt21YxMTEyxujLL7/MdnnuVmYzt9KFRQAAgALEjBMAAIBFBCcAAACLCE4AAAAWEZwAAAAsIjgBAABYRHACAACwiOAEAABgEcEJAADAIoITAACARQQnAAAAiwhOAAAAFhGcAAAALPr/ANLl+0TcSa1gAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "import matplotlib.pyplot as plt\n", - "import pandas as pd\n", - "\n", "df = pd.read_csv(\"../results/norm_temp/norm_temp.tsv\", sep=\"\\t\")\n", "\n", "before = df[\"raw_temperature\"].nunique()\n", @@ -72,7 +84,7 @@ " linewidth=1.2,\n", ")\n", "\n", - "for bar, val in zip(bars, [before, after]):\n", + "for bar, val in zip(bars, [before, after], strict=False):\n", " ax.text(\n", " bar.get_x() + bar.get_width() / 2,\n", " bar.get_height() + 0.5,\n", @@ -89,49 +101,15 @@ " \"Temperature normalization\", fontsize=13, fontweight=\"500\", color=\"black\", pad=14\n", ")\n", "ax.set_ylim(0, max(before, after) * 1.2)\n", - "ax.grid(False)\n", + "ax.grid(visible=False)\n", "ax.spines[[\"top\", \"right\"]].set_visible(False)\n", "ax.spines[\"left\"].set_color(\"black\")\n", "ax.spines[\"bottom\"].set_color(\"black\")\n", "ax.tick_params(colors=\"black\", labelsize=10)\n", "\n", "plt.tight_layout()\n", - "plt.savefig(\"histo_temp.png\", dpi=150, bbox_inches=\"tight\")\n", "print(\"OK\")" ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "c115440c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "370" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df = pd.read_csv(\"../data/entities.tsv\", sep=\"\\t\")\n", - "NOT_WANTED_WORD = [\"inhibitor\", \"agonist\", \"activator\"]\n", - "\n", - "mol_entities = df[df[\"category\"] == \"MOL\"]\n", - "mol_entities = list(mol_entities[\"entity\"].unique())\n", - "molecule_liste = []\n", - "for molecule in mol_entities:\n", - " if len(molecule) > 3:\n", - " for word in NOT_WANTED_WORD:\n", - " if word not in molecule:\n", - " molecule_liste.append(molecule)\n", - "molecule_liste = list(set(molecule_liste))\n", - "len(molecule_liste)" - ] } ], "metadata": { diff --git a/notebooks/visualize_time_simu.ipynb b/notebooks/visualize_STIME.ipynb similarity index 87% rename from notebooks/visualize_time_simu.ipynb rename to notebooks/visualize_STIME.ipynb index e9bd688..a4e046c 100644 --- a/notebooks/visualize_time_simu.ipynb +++ b/notebooks/visualize_STIME.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 15, "id": "92adb128", "metadata": {}, "outputs": [], @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "id": "a894cadc", "metadata": {}, "outputs": [], @@ -29,7 +29,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 17, "id": "138da561", "metadata": {}, "outputs": [ @@ -103,7 +103,7 @@ "4 1 microsecond 1.0 μs" ] }, - "execution_count": 4, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -114,7 +114,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 18, "id": "9b2ebc3d", "metadata": {}, "outputs": [], @@ -125,7 +125,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "id": "156223d8", "metadata": {}, "outputs": [ @@ -135,7 +135,7 @@ "111" ] }, - "execution_count": 17, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -146,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "id": "9755418d", "metadata": {}, "outputs": [ @@ -156,7 +156,7 @@ "88" ] }, - "execution_count": 18, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -167,7 +167,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "id": "cell-7", "metadata": {}, "outputs": [ @@ -227,105 +227,8 @@ "ax.set_title(\"Distribution of simulation times\")\n", "ax.legend()\n", "plt.tight_layout()\n", - "plt.savefig(\"stime_distribution.png\")\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "475d25ab", - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'valid_ns_llm' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m global_min = \u001b[38;5;28mmin\u001b[39m(\u001b[38;5;28mmin\u001b[39m(\u001b[43mvalid_ns_llm\u001b[49m), \u001b[38;5;28mmin\u001b[39m(valid_ns_reg))\n\u001b[32m 2\u001b[39m global_max = \u001b[38;5;28mmax\u001b[39m(\u001b[38;5;28mmax\u001b[39m(valid_ns_llm), \u001b[38;5;28mmax\u001b[39m(valid_ns_reg))\n\u001b[32m 3\u001b[39m shared_bins = np.logspace(np.log10(global_min), np.log10(global_max), \u001b[32m30\u001b[39m)\n", - "\u001b[31mNameError\u001b[39m: name 'valid_ns_llm' is not defined" - ] - } - ], - "source": [ - "global_min = min(min(valid_ns_llm), min(valid_ns_reg))\n", - "global_max = max(max(valid_ns_llm), max(valid_ns_reg))\n", - "shared_bins = np.logspace(np.log10(global_min), np.log10(global_max), 30)\n", - "\n", - "fig, ax = plt.subplots(figsize=(10, 6))\n", - "\n", - "ax.hist(\n", - " valid_ns_llm,\n", - " bins=shared_bins,\n", - " color=\"royalblue\",\n", - " alpha=0.45,\n", - " linewidth=2,\n", - " hatch=\"///\",\n", - " label=\"AI-based STIME extraction (LLM)\",\n", - ")\n", - "ax.hist(\n", - " valid_ns_llm,\n", - " bins=shared_bins,\n", - " histtype=\"step\",\n", - " edgecolor=\"navy\",\n", - " linewidth=2.0,\n", - ")\n", - "ax.hist(\n", - " valid_ns_reg,\n", - " bins=shared_bins,\n", - " color=\"gold\",\n", - " edgecolor=\"darkgoldenrod\",\n", - " alpha=0.65,\n", - " linewidth=2,\n", - " label=\"Naïve STIME extraction (regex)\",\n", - ")\n", - "\n", - "ax.set_xscale(\"log\")\n", - "ax.set_xticks([1e0, 1e3, 1e6])\n", - "ax.set_xticklabels([\"1 ns\", \"1 μs\", \"1 ms\"])\n", - "ax.set_xlabel(\"Simulation time\")\n", - "ax.set_ylabel(\"Count\")\n", - "ax.set_title(\"Distribution of simulation times\")\n", - "ax.legend()\n", - "plt.tight_layout()\n", - "plt.savefig(\"stime_distribution.png\")\n", "plt.show()" ] - }, - { - "cell_type": "code", - "execution_count": 45, - "id": "29054502", - "metadata": {}, - "outputs": [ - { - "ename": "ParserError", - "evalue": "Error tokenizing data. C error: Expected 1 fields in line 48, saw 2\n", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mParserError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[45]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m df = \u001b[43mpd\u001b[49m\u001b[43m.\u001b[49m\u001b[43mread_csv\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m../data/entities.tsv\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 2\u001b[39m df = [df[\u001b[33m\"\u001b[39m\u001b[33mcategory\u001b[39m\u001b[33m\"\u001b[39m] == \u001b[33m\"\u001b[39m\u001b[33mSOFTNAMES\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m 3\u001b[39m \u001b[38;5;28mlen\u001b[39m(df)\n", - "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:873\u001b[39m, in \u001b[36mread_csv\u001b[39m\u001b[34m(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, skip_blank_lines, parse_dates, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, low_memory, memory_map, float_precision, storage_options, dtype_backend)\u001b[39m\n\u001b[32m 861\u001b[39m kwds_defaults = _refine_defaults_read(\n\u001b[32m 862\u001b[39m dialect,\n\u001b[32m 863\u001b[39m delimiter,\n\u001b[32m (...)\u001b[39m\u001b[32m 869\u001b[39m dtype_backend=dtype_backend,\n\u001b[32m 870\u001b[39m )\n\u001b[32m 871\u001b[39m kwds.update(kwds_defaults)\n\u001b[32m--> \u001b[39m\u001b[32m873\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilepath_or_buffer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:306\u001b[39m, in \u001b[36m_read\u001b[39m\u001b[34m(filepath_or_buffer, kwds)\u001b[39m\n\u001b[32m 303\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m parser\n\u001b[32m 305\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m parser:\n\u001b[32m--> \u001b[39m\u001b[32m306\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mparser\u001b[49m\u001b[43m.\u001b[49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnrows\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/readers.py:1947\u001b[39m, in \u001b[36mTextFileReader.read\u001b[39m\u001b[34m(self, nrows)\u001b[39m\n\u001b[32m 1940\u001b[39m nrows = validate_integer(\u001b[33m\"\u001b[39m\u001b[33mnrows\u001b[39m\u001b[33m\"\u001b[39m, nrows)\n\u001b[32m 1941\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 1942\u001b[39m \u001b[38;5;66;03m# error: \"ParserBase\" has no attribute \"read\"\u001b[39;00m\n\u001b[32m 1943\u001b[39m (\n\u001b[32m 1944\u001b[39m index,\n\u001b[32m 1945\u001b[39m columns,\n\u001b[32m 1946\u001b[39m col_dict,\n\u001b[32m-> \u001b[39m\u001b[32m1947\u001b[39m ) = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_engine\u001b[49m\u001b[43m.\u001b[49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[attr-defined]\u001b[39;49;00m\n\u001b[32m 1948\u001b[39m \u001b[43m \u001b[49m\u001b[43mnrows\u001b[49m\n\u001b[32m 1949\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1950\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m:\n\u001b[32m 1951\u001b[39m \u001b[38;5;28mself\u001b[39m.close()\n", - "\u001b[36mFile \u001b[39m\u001b[32m/data/zenati/mdverse_entity_norm/.venv/lib/python3.12/site-packages/pandas/io/parsers/c_parser_wrapper.py:215\u001b[39m, in \u001b[36mCParserWrapper.read\u001b[39m\u001b[34m(self, nrows)\u001b[39m\n\u001b[32m 213\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 214\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.low_memory:\n\u001b[32m--> \u001b[39m\u001b[32m215\u001b[39m chunks = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_reader\u001b[49m\u001b[43m.\u001b[49m\u001b[43mread_low_memory\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnrows\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 216\u001b[39m \u001b[38;5;66;03m# destructive to chunks\u001b[39;00m\n\u001b[32m 217\u001b[39m data = _concatenate_chunks(chunks, \u001b[38;5;28mself\u001b[39m.names)\n", - "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/parsers.pyx:832\u001b[39m, in \u001b[36mpandas._libs.parsers.TextReader.read_low_memory\u001b[39m\u001b[34m()\u001b[39m\n", - "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/parsers.pyx:897\u001b[39m, in \u001b[36mpandas._libs.parsers.TextReader._read_rows\u001b[39m\u001b[34m()\u001b[39m\n", - "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/parsers.pyx:868\u001b[39m, in \u001b[36mpandas._libs.parsers.TextReader._tokenize_rows\u001b[39m\u001b[34m()\u001b[39m\n", - "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/parsers.pyx:885\u001b[39m, in \u001b[36mpandas._libs.parsers.TextReader._check_tokenize_status\u001b[39m\u001b[34m()\u001b[39m\n", - "\u001b[36mFile \u001b[39m\u001b[32mpandas/_libs/parsers.pyx:2084\u001b[39m, in \u001b[36mpandas._libs.parsers.raise_parser_error\u001b[39m\u001b[34m()\u001b[39m\n", - "\u001b[31mParserError\u001b[39m: Error tokenizing data. C error: Expected 1 fields in line 48, saw 2\n" - ] - } - ], - "source": [ - "df = pd.read_csv(\"../data/entities.tsv\")\n", - "df = [df[\"category\"] == \"SOFTNAMES\"]\n", - "len(df)" - ] } ], "metadata": { From 42ea8aa7d82bcd44108d827f717d3e28253c2ce4 Mon Sep 17 00:00:00 2001 From: essmaw Date: Mon, 6 Jul 2026 12:57:39 +0200 Subject: [PATCH 11/26] refactor: move grounding logic diagram in `plots`folder and update README reference. --- README.md | 2 +- .../molecules_grounding_logic.dot | 0 .../molecules_grounding_logic.png | Bin 3 files changed, 1 insertion(+), 1 deletion(-) rename molecules_grounding_logic.dot => plots/molecules_grounding_logic.dot (100%) rename molecules_grounding_logic.png => plots/molecules_grounding_logic.png (100%) diff --git a/README.md b/README.md index d64b404..9911de3 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Special cases `room temperature` and `human body temperature` are normalised to The grounding logic is illustrated below: -![Grounding logic](molecules_grounding_logic.png) +![Grounding logic](plots/molecules_grounding_logic.png) ```sh uv run src/mdverse_entity_norm/scripts/normalize_molecules.py diff --git a/molecules_grounding_logic.dot b/plots/molecules_grounding_logic.dot similarity index 100% rename from molecules_grounding_logic.dot rename to plots/molecules_grounding_logic.dot diff --git a/molecules_grounding_logic.png b/plots/molecules_grounding_logic.png similarity index 100% rename from molecules_grounding_logic.png rename to plots/molecules_grounding_logic.png From 84fa6f035b840f88e0ce74cf6b3b9066b44a5fa0 Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 13:02:27 +0200 Subject: [PATCH 12/26] chore: delete unused files and rename llm_errors script --- src/mdverse_entity_norm/.gitignore | 1 - ...rrors.py => count_llm_errors_from_logs.py} | 2 + .../scripts/scrap_lipids.py | 411 ------------------ 3 files changed, 2 insertions(+), 412 deletions(-) delete mode 100644 src/mdverse_entity_norm/.gitignore rename src/mdverse_entity_norm/scripts/{llm_errors.py => count_llm_errors_from_logs.py} (97%) delete mode 100644 src/mdverse_entity_norm/scripts/scrap_lipids.py diff --git a/src/mdverse_entity_norm/.gitignore b/src/mdverse_entity_norm/.gitignore deleted file mode 100644 index cb16962..0000000 --- a/src/mdverse_entity_norm/.gitignore +++ /dev/null @@ -1 +0,0 @@ -notebooks/tests.ipynb diff --git a/src/mdverse_entity_norm/scripts/llm_errors.py b/src/mdverse_entity_norm/scripts/count_llm_errors_from_logs.py similarity index 97% rename from src/mdverse_entity_norm/scripts/llm_errors.py rename to src/mdverse_entity_norm/scripts/count_llm_errors_from_logs.py index ab1ca41..967e5e6 100644 --- a/src/mdverse_entity_norm/scripts/llm_errors.py +++ b/src/mdverse_entity_norm/scripts/count_llm_errors_from_logs.py @@ -1,3 +1,5 @@ +"""Script to count the number of LLM errors from logs.""" + import sys import pandas as pd diff --git a/src/mdverse_entity_norm/scripts/scrap_lipids.py b/src/mdverse_entity_norm/scripts/scrap_lipids.py deleted file mode 100644 index a5a9ba8..0000000 --- a/src/mdverse_entity_norm/scripts/scrap_lipids.py +++ /dev/null @@ -1,411 +0,0 @@ -"""Scraps CSML molecules""" - -import csv -import os -import re -from datetime import UTC, datetime -from pathlib import Path - -import click -import httpx -import pandas as pd -from bs4 import BeautifulSoup -from ground_molecule import call_chebi, call_pubchem -from loguru import logger - -URL = "https://charmm-gui.org/?doc=archive&lib=csml" -PAGE = httpx.get(URL) -SOUP = BeautifulSoup(PAGE.content, "html.parser") - - -def get_lipid_family_name(th) -> str | None: - """Give the family name from a tag. - - Parameters - ---------- - th : beautifulsoup tag - Represent the element. - - Returns - ------- - str | None - The text in bold -> the family name - """ - bold_tag = th.find("b") - if bold_tag: - return bold_tag.get_text(strip=True) - return None - - -def build_grounding_name(short_name, long_name): - """Build the grounding_name from the short_name and long_name. - - Parameters - ---------- - short_name : str - The short name of the lipid/residue. - long_name : str - The long name of the lipid/residue - - Returns - ------- - str - The grounding name, built from the short name and the long name(cleaned). - """ - long_name = long_name.replace("+ LONEPAIR", "").strip() - long_name = long_name.replace("+ LONE PAIR", "").strip() - elements = [] - formula_regex = re.compile(r"^[A-Z]+[0-9]+([A-Z]{1,2}[0-9]*)*$") - - # We split the long name on ", " - if long_name: - for e in long_name.split(", "): - if e: - elements.append(e) - - formula = None - full_name = None - - if len(elements) == 0: - pass - - # If there is only one element, we check if it's a formula or a full name - elif len(elements) == 1: - if formula_regex.match(elements[0]): - formula = elements[0] - else: - logger.info(f"For {elements} adding full name '{elements[0]}'") - full_name = elements[0] - - else: - # If there are multiple elements, we check if the first one is a formula or a full name - if formula_regex.match(elements[0]): - formula = elements[0] - logger.info(f"For {elements} adding full name '{elements[0]}'") - - full_name = elements[1] - else: - logger.info(f"For {elements} adding full name '{elements[0]}'") - full_name = elements[0] - - # We create the grounding name - grounding_name = [] - # if short_name: - # grounding_name.append(short_name) - # if formula: - # grounding_name.append(formula) - if full_name: - logger.info( - f"Adding full name '{full_name}' to grounding name for short name '{short_name}'" - ) - grounding_name.append(full_name) - - return " ".join(grounding_name), formula - - -def parse_data_row(row, current_family): - """Extract short name and long name from a data row. - - Parameters - ---------- - row : beautifulsoup tag - Represent the element containing the data ( = row). - current_family : str - The family name associated with the current data row, extracted from the header. - - Returns - ------- - dict | None - A dictionary with keys 'family', 'short_name', 'long_name', and 'grounding_name' - or None if there is not valid informations. - """ - cells = row.find_all("td") - if len(cells) < 2: - return None - - # We extract the short name from the first cell. If it's empty, we skip this row. - short_name = cells[0].get_text(strip=True) - if not short_name: - return None - # looks for a tag inside the second cell to extract the long name, - # because some long names are in a tag. - font_tag = cells[1].find("font") - if font_tag: - long_name = font_tag.get_text(strip=True) - else: - long_name = cells[1].get_text(strip=True) - if not long_name: - return None - - query_name, formula = build_grounding_name(short_name, long_name) - - return { - "short_name": short_name, - "formula": formula, - "long_name": long_name, - "query_name": query_name, - } - - -def scrape_table(soup): - """Scrape the table from the webpage and extract the relevant data. - - Parameters - ---------- - soup : BeautifulSoup - The BeautifulSoup object representing the parsed HTML content of the webpage. - - Returns - ------- - list of dict - A list of dictionaries, each containing the family, short name, long name, - and grounding name for each lipid/residue entry found in the table. - """ - results = [] - for header_th in soup.find_all("th", class_="header"): - current_family = get_lipid_family_name(header_th) - if not current_family: - continue - - tbody = header_th.find_next_sibling("tbody") - if not tbody: - continue - - for row in tbody.find_all("tr"): - entry = parse_data_row(row, current_family) - if entry: - results.append(entry) - - return results - - -def save_to_csv(results, filename): - """Save the scrapped data to a CSV file. - - Parameters - ---------- - results : list of dict - The list of dictionaries containing the scrapped data. - filename : Path - The path to the output CSV file where the scraped data will be saved. - """ - Path(filename).parent.mkdir(parents=True, exist_ok=True) - scrapping_results = pd.DataFrame(results) - scrapping_results.to_csv(filename, index=False) - - -def ground_one(grounding_name: str) -> dict: - """Try to ground a single grounding_name via ChEBI then PubChem. - - Parameters - ---------- - grounding_name : str - The grounding name to look up. - - Returns - ------- - dict - A dict with keys: MOL, MOL_TYPE, ERRORS, MOL_ID, MOL_SCORE, MOL_FULL_NAME - """ - result = call_chebi(grounding_name) - if result and "error" not in result: - return { - "MOL": grounding_name, - "MOL_TYPE": "CHEBI", - "ERRORS": "No errors", - "MOL_ID": result.get("chebi_id", "Not Available"), - "MOL_SCORE": result.get("score", "Not Available"), - "MOL_FULL_NAME": result.get("name", "Not Available"), - } - - result = call_pubchem(grounding_name) - if result and "error" not in result: - return { - "MOL": grounding_name, - "MOL_TYPE": "PUBCHEM", - "ERRORS": "No errors", - "MOL_ID": result.get("id", "Not Available"), - "MOL_SCORE": "Not Available", - "MOL_FULL_NAME": result.get("name", "Not Available"), - } - - return { - "MOL": grounding_name, - "MOL_TYPE": "Not found", - "ERRORS": "No match in ChEBI or PubChem", - "MOL_ID": "Not Available", - "MOL_SCORE": "Not Available", - "MOL_FULL_NAME": "Not Available", - } - - -def ground_both( - grounding_name: str, short_name: str, long_name: str, formula: str -) -> dict: - """Ground a single grounding_name via ChEBI and PubChem. - - Parameters - ---------- - grounding_name : str - The grounding name to look up. - short_name : str - The short name of the lipid/residue. - long_name : str - The long name of the lipid/residue. - formula : str - The chemical formula of the lipid/residue. - - Returns - ------- - dict - A dict with keys: short_name, formula, long_name, chebi_query, pubchem_query, - chebi_id, chebi_score, chebi_name, pubchem_id, pubchem_name - """ - chebi = call_chebi(grounding_name) or {} - pubchem = call_pubchem(grounding_name) or {} - - chebi_ok = ( - chebi.get("chebi_id") and chebi.get("formula", "Not Available") == formula - ) - pubchem_ok = ( - pubchem.get("id") - and pubchem.get("molecular_formula", "Not Available") == formula - ) - - if not chebi_ok: - if short_name != grounding_name: - chebi_fallback = call_chebi(short_name) or {} - if ( - chebi_fallback.get("chebi_id") - and chebi_fallback.get("formula", "Not Available") == formula - ): - chebi = chebi_fallback - chebi_ok = True - chebi_query = short_name - else: - chebi_query = grounding_name - - if not pubchem_ok: - if short_name != grounding_name: - pubchem_fallback = call_pubchem(short_name) or {} - if ( - pubchem_fallback.get("id") - and pubchem_fallback.get("molecular_formula", "Not Available") - == formula - ): - pubchem = pubchem_fallback - pubchem_ok = True - pubchem_query = short_name - else: - pubchem_query = grounding_name - - return { - "short_name": short_name, - "formula": formula, - "long_name": long_name, - "chebi_query": chebi_query, - "pubchem_query": pubchem_query, - "chebi_id": chebi.get("chebi_id", "Not found") if chebi_ok else "Not found", - "chebi_score": chebi.get("score", "Not found") if chebi_ok else "Not found", - "chebi_name": chebi.get("name", "Not found") if chebi_ok else "Not found", - "pubchem_id": pubchem.get("id", "Not found") if pubchem_ok else "Not found", - "pubchem_name": pubchem.get("name", "Not found") if pubchem_ok else "Not found", - } - - -def _grounding_sort_key(entry: dict) -> int: - """Sort grounding results, prioritizing entries that were found in either ChEBI or PubChem. - - Parameters - ---------- - entry : dict - A dict with keys: query_name, chebi_id, chebi_score, chebi_name, pubchem_id, pubchem_name - - Returns - ------- - int - 0 if the entry was found in either ChEBI or PubChem, 1 otherwise. - """ - found = entry["chebi_id"] != "Not found" or entry["pubchem_id"] != "Not found" - return 0 if found else 1 - - -def save_grounding_to_tsv(grounding_results: list[dict], output_file: Path) -> None: - """Save grounding results to a TSV file. - - Parameters - ---------- - grounding_results : list[dict] - List of grounding result dicts, one per lipid. - output_file : Path - Path to the output TSV file. - """ - Path(output_file).parent.mkdir(parents=True, exist_ok=True) - with open(output_file, "w", newline="") as f: - writer = csv.DictWriter( - f, - fieldnames=[ - "short_name", - "formula", - "long_name", - "chebi_query", - "pubchem_query", - "chebi_id", - "chebi_score", - "chebi_name", - "pubchem_id", - "pubchem_name", - ], - delimiter="\t", - ) - writer.writeheader() - writer.writerows(grounding_results) - - -@click.command() -@click.option( - "--scraped_file", - default="results/lipid_scrapping/csml_lipids.csv", - type=click.Path(file_okay=True, path_type=Path), - help="Path to the input file containing molecular identifiers", -) -@click.option( - "--grounded_file", - default="results/lipid_scrapping/csml_lipids_grounded.tsv", - type=click.Path(file_okay=True, path_type=Path), - help="Path to the output TSV file with grounding results", -) -def scrap_lipids(scraped_file, grounded_file): - """Scrape the lipid/residue data from the webpage and save it to a CSV file. - - Parameters - ---------- - scraped_file : Path - The path to the output CSV file where the scraped data will be saved. - grounded_file : Path - The path to the output TSV file where the grounding results will be saved. - """ - lipid_scrapping = scrape_table(SOUP) - save_to_csv(lipid_scrapping, scraped_file) - - lipid_grounding_results = [] - for entry in lipid_scrapping: - grounded = ground_both( - grounding_name=entry["query_name"], - short_name=entry["short_name"], - formula=entry["formula"], - long_name=entry["long_name"], - ) - lipid_grounding_results.append(grounded) - lipid_grounding_results.sort(key=_grounding_sort_key) - save_grounding_to_tsv(lipid_grounding_results, grounded_file) - - -if __name__ == "__main__": - timestamp = datetime.now(UTC).strftime("%Y%m%d_%H%M%S") - os.makedirs("logs", exist_ok=True) - logger.add( - f"logs/ground_molecule_{timestamp}.log", - level="DEBUG", - ) - scrap_lipids() From 435c69550733e9d6cb0736845155621e953a8232 Mon Sep 17 00:00:00 2001 From: essmaw Date: Mon, 6 Jul 2026 15:35:27 +0200 Subject: [PATCH 13/26] refactor(`count_llm_errors_from_logs.py`): simplify the code and add click for CLI. --- .../scripts/count_llm_errors_from_logs.py | 92 ++++++++++--------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/src/mdverse_entity_norm/scripts/count_llm_errors_from_logs.py b/src/mdverse_entity_norm/scripts/count_llm_errors_from_logs.py index 967e5e6..f75bc60 100644 --- a/src/mdverse_entity_norm/scripts/count_llm_errors_from_logs.py +++ b/src/mdverse_entity_norm/scripts/count_llm_errors_from_logs.py @@ -1,27 +1,23 @@ """Script to count the number of LLM errors from logs.""" -import sys +from pathlib import Path +import click import pandas as pd from loguru import logger -def check_argument(): - if len(sys.argv) < 2: - print("Usage: uv run script.py ") - sys.exit(1) +def extract_errors(lines: list[str]) -> list: + """Extract the model, input, and output from the log lines. - file_name = sys.argv[1] - - with open(file_name) as f: - lines = f.readlines() - - return lines - - -def extract_errors(lines): + Returns + ------- + list + A list of lists, where each inner list contains the model, input, and output for + each error found in the log lines. + """ results = [] - logger.info("Extarcting input and output") + logger.info("Extracting errors from log lines...") for i in range(len(lines)): if "failed" in lines[i]: context = lines[max(0, i - 2) : i + 1] @@ -50,41 +46,47 @@ def extract_errors(lines): results.append([model, input_val, output]) + logger.success(f"Extracted {len(results)} errors from log lines.") return results -def compute_occurrences(df): - counts = df.value_counts().reset_index() +@click.command() +@click.option( + "--log-file", + type=click.Path(exists=True, dir_okay=False, path_type=Path), + help="Path to the TSV file containing the entities.", +) +def main(log_file: Path): + """Count the number of LLM errors from logs.""" + logger.info(f"Counting LLM errors from log file: {log_file}") + # Load the log file and extract the lines + try: + with open(log_file, encoding="utf-8") as f: + lines = f.readlines() + except FileNotFoundError: + logger.error("Log file not found.") + return + except UnicodeDecodeError as e: + logger.error(f"Error reading log file: {e}") + return + + errors = extract_errors(lines) + df_errors = pd.DataFrame(errors, columns=["model", "input", "result"]) + # Count the occurrences of each unique combination of model, input, and result + counts = df_errors.value_counts().reset_index() counts.columns = ["model", "input", "result", "count"] - return counts - - -def add_model_totals(df): - df["total"] = df.groupby("model")["count"].transform("sum") - return df - - -def sort_results(df): - return df.sort_values(by=["total", "count"], ascending=False) - - -def save_to_tsv(df, output_path="results/norm_simu_times/llm_error_count.tsv"): - df.to_csv(output_path, sep="\t", index=False) - - -def main(): - lines = check_argument() - - data = extract_errors(lines) - df = pd.DataFrame(data, columns=["model", "input", "result"]) - - occurrences = compute_occurrences(df) - occurrences = add_model_totals(occurrences) - sorted_df = sort_results(occurrences) + # Add a total count for each model + counts["total"] = counts.groupby("model")["count"].transform("sum") + # Sort the DataFrame by total count and then by individual count + sorted_df = counts.sort_values(by=["total", "count"], ascending=False) + # Rearrange the columns to have total and count first sorted_df = sorted_df[["total", "count", "model", "input", "result"]] - - print(sorted_df) - save_to_tsv(sorted_df) + logger.debug(f"Sorted DataFrame:\n{sorted_df}") + # Save the sorted DataFrame to a TSV file + output_path = Path("results/STIME_normalized/llm_error_count.tsv") + if not output_path.parent.exists(): + output_path.parent.mkdir(parents=True, exist_ok=True) + sorted_df.to_csv(output_path, sep="\t", index=False) if __name__ == "__main__": From f2b62299a345591674103781854a9265bc53ecb3 Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 15:39:05 +0200 Subject: [PATCH 14/26] fix: change keys to avoid errors --- .../scripts/normalize_simulation_time.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/mdverse_entity_norm/scripts/normalize_simulation_time.py b/src/mdverse_entity_norm/scripts/normalize_simulation_time.py index 917c2c9..b4fb460 100644 --- a/src/mdverse_entity_norm/scripts/normalize_simulation_time.py +++ b/src/mdverse_entity_norm/scripts/normalize_simulation_time.py @@ -320,7 +320,12 @@ def normalize_all_entities( return normalised_entity, normalisation_time, normalisation_cost -def evaluate_all_models(raw_simulation_times: list, ground_truth_file: Path, runs: int, prompt_file_path: Path): +def evaluate_all_models( + raw_simulation_times: list, + ground_truth_file: Path, + runs: int, + prompt_file_path: Path, +): """Evaluate all models and save results to TSV file. Parameters @@ -412,13 +417,15 @@ def save_evaluation_results_in_tsv( runs (int): The number of runs to perform for each model to calculate the average accuracy. """ - results = evaluate_all_models(raw_simulation_times, ground_truth_file, runs, prompt_file_path) + results = evaluate_all_models( + raw_simulation_times, ground_truth_file, runs, prompt_file_path + ) with open(model_evaluation_file, "w") as f: f.write( "model_name\taccuracy_percentage\tnormalisation_times_sec\tnormalisation_cost\n" ) f.writelines( - f"{result['model_name']}\t{result['accuracy_percentage']}\t{result['normalisation_time']}\t{result['normalisation_cost']}\n" + f"{result['model_name']}\t{result['accuracy_percentage']}\t{result['inference_time_by_entity']}\t{result['inference_cost_by_entity_USD']}\n" for result in results ) From 7584423a6b7ff6a21d24efeca825e5bc84931f37 Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 15:41:46 +0200 Subject: [PATCH 15/26] chore: rename file for stime normalisation --- .../{normalize_simulation_time.py => evaluate_llm_models.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/mdverse_entity_norm/scripts/{normalize_simulation_time.py => evaluate_llm_models.py} (100%) diff --git a/src/mdverse_entity_norm/scripts/normalize_simulation_time.py b/src/mdverse_entity_norm/scripts/evaluate_llm_models.py similarity index 100% rename from src/mdverse_entity_norm/scripts/normalize_simulation_time.py rename to src/mdverse_entity_norm/scripts/evaluate_llm_models.py From 3ac6840c70bd9b208f7d06c4b43dda0a799428fb Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 16:03:34 +0200 Subject: [PATCH 16/26] chore: update ReadMe --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9911de3..4ece255 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # mdverse_entity_norm +This project implements the normalization pipeline for molecular dynamics (MD) simulation metadata entities. Normalization standardizes entity mentions by mapping them to controlled vocabularies or reference databases (e.g., [ChEBI](https://www.ebi.ac.uk/chebi/), [PubChem](https://pubchem.ncbi.nlm.nih.gov/), [KEGG](https://www.genome.jp/kegg/)), ensuring consistency and interoperability across datasets. +Normalisation is currently supported for four entity types: molecule names (MOL),simulation times (STIME) and temperatures (STEMP), software names (SOFTNAME) and force fields and models (FFM). + ## Setup environment We use [uv](https://docs.astral.sh/uv/getting-started/installation/) to manage dependencies and the project environment. @@ -19,12 +22,13 @@ uv sync ## Usage -This project implements the normalisation pipeline for molecular dynamics simulation metadata entities. Normalisation is currently supported for four entity types: temperature, small molecules, simulation times, and software versions (not yet implemented). The scripts are located in `src/mdverse_entity_norm/scripts/` and can be executed independently. Output files are saved in the `results/` directory, which is created automatically if it does not exist. +The following scripts require an `entities.tsv` file as input. The file should contain the columns `entity`, `category`, and `json_file`. -### Normalize temperature +### Simulation temperature (STEMP) +To normalize simulation temperatures, run: ```sh -uv run src/mdverse_entity_norm/scripts/normalize_temperature.py +uv run src/mdverse_entity_norm/scripts/normalize_stemp.py ``` This reads temperature entities from `data/entities.tsv` and writes `results/norm_temp.tsv`, a TSV file with four columns: @@ -38,7 +42,7 @@ This reads temperature entities from `data/entities.tsv` and writes `results/nor Special cases `room temperature` and `human body temperature` are normalised to 293 K and 310 K respectively. All Celsius values are converted to Kelvin. -### Ground molecules +### Ground molecule names The grounding logic is illustrated below: From be7d0db484b62af6868eca1f211d60b3d32e2c76 Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 16:08:18 +0200 Subject: [PATCH 17/26] chore: change file name and clean the code --- ...lize_temperature.py => normalize_stemp.py} | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) rename src/mdverse_entity_norm/scripts/{normalize_temperature.py => normalize_stemp.py} (88%) diff --git a/src/mdverse_entity_norm/scripts/normalize_temperature.py b/src/mdverse_entity_norm/scripts/normalize_stemp.py similarity index 88% rename from src/mdverse_entity_norm/scripts/normalize_temperature.py rename to src/mdverse_entity_norm/scripts/normalize_stemp.py index e482917..5f1541b 100644 --- a/src/mdverse_entity_norm/scripts/normalize_temperature.py +++ b/src/mdverse_entity_norm/scripts/normalize_stemp.py @@ -1,7 +1,4 @@ -"""Module for using regex. - -This module provides regular expression matching operations. -""" +"""Script to normalize simulation temperature entities.""" import re @@ -35,7 +32,7 @@ def norm_temp(temp_str: str) -> tuple: # an optional unit because of the "?" symbol at the end of the group. # This group consists of zero or more spaces, because of the "*" symbol, # an optional degree symbol, then zero or more spaces, and zero or more letters. - logger.info("Normalising temperature entities ...") + logger.info("Normalising temperature entities...") # If the temperatue is anotated as room temperature or body temperature # we normalize it to the standard value if temp_str == "room temperature": @@ -71,7 +68,7 @@ def norm_temp(temp_str: str) -> tuple: return temperature_value, temperature_unit -def create_norm_temp_file(raw_temp_file: str, norm_temp_file: str): +def create_norm_temp_file(raw_temp_file: str, norm_temp_file: str) -> None: """Create a .tsv file containing the raw temperature value. the normalised temperature value and the normalised unit. @@ -95,14 +92,20 @@ def create_norm_temp_file(raw_temp_file: str, norm_temp_file: str): if temperature_value is not None: f2.write( - f"{raw_temp}\t{temperature_value}\t{temperature_unit}\t{str(temperature_value) + temperature_unit}\n" + f"{raw_temp}\t{temperature_value}\t{temperature_unit}" + f"\t{str(temperature_value) + temperature_unit}\n" ) else: f2.write(f"{raw_temp}\tERROR\tERROR\tERROR\n") def visualize_entity_count(file_path): + """Visualize the number of unique temperature entities before/after normalization. + Parameters + ---------- + file_path (str): Path to the TSV file containing the normalized temperature results. + """ temp_normalisation_results = pd.read_csv(file_path, sep="\t") before = len(temp_normalisation_results["raw_temperature"].unique()) @@ -114,7 +117,6 @@ def visualize_entity_count(file_path): values = [before, after] plt.bar(labels, values) - plt.ylabel("Number of unique temperatures") plt.title("Unique Temperature Count") plt.tight_layout() @@ -122,20 +124,5 @@ def visualize_entity_count(file_path): if __name__ == "__main__": - # Testing different cases of temp normalisation - examples_temperature = [ - "300", - "300 k", - "27", - "300k", - "0c", - "37 celsius", - "37°C", - "310.15°K", - "20 Celsius", - ] - for temperature in examples_temperature: - print(f"norm_temp('{temperature}') = {norm_temp(temperature)}") - create_norm_temp_file("data/entities.tsv", "results/norm_temp.tsv") visualize_entity_count("results/norm_temp.tsv") From ed3349ebb90248c8cde02057efb1be9db8b2b801 Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 16:51:57 +0200 Subject: [PATCH 18/26] chores: clean and update stime normalisation scripts --- .../scripts/evaluate_llm_models.py | 65 ++++++------------- .../scripts/normalize_stime_wth_llm.py | 2 +- 2 files changed, 21 insertions(+), 46 deletions(-) diff --git a/src/mdverse_entity_norm/scripts/evaluate_llm_models.py b/src/mdverse_entity_norm/scripts/evaluate_llm_models.py index b4fb460..1b2f42e 100644 --- a/src/mdverse_entity_norm/scripts/evaluate_llm_models.py +++ b/src/mdverse_entity_norm/scripts/evaluate_llm_models.py @@ -19,8 +19,8 @@ load_dotenv() -# We deifine the list of model that we are going to test -MODEL = [ +# list of model that we are going to test +MODELS = [ "openai/gpt-4o", "openai/gpt-5.5", "deepseek/deepseek-v4-pro", @@ -32,13 +32,6 @@ "mistralai/mistral-large-2512", ] -# We creat a pydantic class that will define the structure the llm output -# - The SimulationTime class will define the structure of a simulation time : -# value, unit -# - The NormSimuTime class will define the structure of the llm output : -# input (simluation time given to normalize) -# output (structured simulation time -> defined by the previous class) - class SimulationTime(BaseModel): """Define the structure of simulation time entity.""" @@ -60,23 +53,6 @@ class NormSimuTime(BaseModel): ) -# We create the prompt containning the normalisation guideline for the llm -PROMPT = """You are a unit normalization assistant for molecular dynamics simulation times. -Your tasks: -- Convert all time units to standard time abbreviations (ps, ns, μs, ms, s) -- Separate numerical values from time units - -Rules: -- No markdown, no explanation -- Use only standard time units: ps (picoseconds), ns (nanoseconds), μs (microseconds), ms (milliseconds), s (seconds) -- Always separate value and unit (e.g. "500ns" → value: 500, unit: "ns") -- Take in consideration values written in letter (e.g. "one hundred"), and convert it to numeric value -- If the simulation time is an interval, separate each simulation time in the interval. -- If the unit is missing or the unit is not a time unit, output the normalized unit to "None" -- If the numerical value is missing, output the normalized value to "None" - """ - - # We load the simulation times from the file in a list of simulatuion time to enable # slicing the list def load_simulation_times(ground_truth_file: Path) -> list: @@ -350,7 +326,7 @@ def evaluate_all_models( results = [] - for model in MODEL: + for model in MODELS: logger.info("-" * 20) logger.info(f"Model: {model.replace('-', '_')}") total_correct = 0 @@ -432,16 +408,9 @@ def save_evaluation_results_in_tsv( @click.command() @click.option( - "--normalized_simulation_time", - default="results/normalized_simulation_time_gpt.json", - type=click.Path(file_okay=True, path_type=Path), - help="Path to the JSON output file containing the normalized simulation times", -) -@click.option( - "--ground_truth_file", - default="data/STIME_ground_truth.json", + "--groundtruth-path", type=click.Path(exists=True, file_okay=True, path_type=Path), - help="Path to the groundtruth file", + help="Path to the groundtruth file containing manually normalized simulation times", ) @click.option( "--runs", @@ -450,24 +419,30 @@ def save_evaluation_results_in_tsv( help="Number of runs of the script", ) @click.option( - "--model_evaluation_file", - default="results/norm_simu_times/model_evaluation.tsv", + "--model-evaluation-path", type=click.Path(file_okay=True, path_type=Path), help="Path to the TSV file for model evaluation results", ) +@click.option( + "--prompt-path", + type=click.Path(file_okay=True, path_type=Path), + help="Path to the llm prompt file", +) def main_normalizing_simulation_times( - normalized_simulation_time: Path, - ground_truth_file: Path, + groundtruth_path: Path, runs: int, - model_evaluation_file: Path, + model_evaluation_path: Path, + prompt_path: Path, ): """Normalize the simulation times entities bu running all annexe functions.""" - times = load_simulation_times(ground_truth_file) + times = load_simulation_times(groundtruth_path) times = times[:] - # normalisation_output = format_norm_simulation_time(times, model_name=MODEL[0]) - # save_norm_simulation_results(normalisation_output, normalized_simulation_time) save_evaluation_results_in_tsv( - model_evaluation_file, times, ground_truth_file, Path("PROMPT"), runs + model_evaluation_path, + times, + groundtruth_path, + prompt_path, + runs, ) diff --git a/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py b/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py index 9c1f3e2..c56cb01 100644 --- a/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py +++ b/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py @@ -6,7 +6,7 @@ import click import pandas as pd -from mdverse_entity_norm.scripts.normalize_simulation_time import ( +from mdverse_entity_norm.scripts.evaluate_llm_models import ( normalize_simulation_time, ) From e8e6900c16a6bc699c253513975baa6dba2f5ab4 Mon Sep 17 00:00:00 2001 From: essmaw Date: Mon, 6 Jul 2026 16:55:02 +0200 Subject: [PATCH 19/26] docs: update README with detailed instructions for STEMP and STIME normalization processes. --- README.md | 127 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 4ece255..c77d19b 100644 --- a/README.md +++ b/README.md @@ -25,90 +25,107 @@ uv sync The following scripts require an `entities.tsv` file as input. The file should contain the columns `entity`, `category`, and `json_file`. ### Simulation temperature (STEMP) + To normalize simulation temperatures, run: ```sh -uv run src/mdverse_entity_norm/scripts/normalize_stemp.py +uv run src/mdverse_entity_norm/scripts/normalize_stemp.py --raw-entities-path data/entities.tsv --normalized-stemp-path results/STEMP/stemp_normalized.tsv ``` -This reads temperature entities from `data/entities.tsv` and writes `results/norm_temp.tsv`, a TSV file with four columns: +This reads temperature entities from `data/entities.tsv` and writes `results/STEMP/stemp_normalized.tsv`, a TSV file with four columns: -| Column | Description | -|---|---| -| `raw_temperature` | Original temperature string | -| `normalised_temperature` | Numeric value after normalisation | -| `normalised_unit` | Unit after normalisation (Kelvin) | -| `normalized_result` | Concatenated value and unit | +raw_temperature | normalised_temperature | normalised_unit | normalized_result +|---|---|---|---| +| 315 | 315 | K | 315 K +| 20°C | 293,15 | K | 293,15 K +| 310k | 310 | K | 310 K -Special cases `room temperature` and `human body temperature` are normalised to 293 K and 310 K respectively. All Celsius values are converted to Kelvin. +> Special cases `room temperature` and `human body temperature` are normalised to 293 K and 310 K respectively. All Celsius values are converted to Kelvin. -### Ground molecule names +### Simulation times (STIME) -The grounding logic is illustrated below: +The normalization of simulation times is a two-step process: first, we benchmark several candidate Large Language Models (LLMs) against a gold standard dataset to select the best performer; second, we deploy the chosen model to normalize the entire dataset. -![Grounding logic](plots/molecules_grounding_logic.png) +> 🔑 An `OPEN_ROUTER_KEY` environment variable must be set (e.g., via a .env file) to authenticate and authorise API requests to the external LLM providers hosted on OpenRouter. + +#### **Model evaluation:** + +To evaluate candidate LLM models on a labelled gold standard, run: ```sh -uv run src/mdverse_entity_norm/scripts/normalize_molecules.py +uv run src/mdverse_entity_norm/scripts/evaluate_llm_models.py \ + --groundtruth-path data/groundtruth/STIME.json \ + --prompt-path data/llm_prompt.txt \ + --runs 10 \ + --model-evaluation-path results/STIME/model_evaluation.tsv ``` -This reads molecular entities from `data/entities.tsv`. Entities are first classified by type (PDB, UniProt, DNA, RNA, protein, or small molecule). PDB and UniProt entries are resolved via their respective APIs and saved to `results/ground_molecule/same_grounding_mol/pdb_uniprot_seq_entities.tsv`. Small molecules are grounded by consensus across ChEBI, PubChem, and KEGG, producing two output files: +This script benchmarks 9 models accessible via OpenRouter (including `GPT-4o`, `DeepSeek V4 Pro`, and `Claude 4.7 Opus`) against a manually annotated gold standard of 100 simulation time entities. The evaluation is repeated over the specified number of runs to ensure statistical robustness. -**`chebi_comparaison.tsv`** — ChEBI grounding results for all small molecules: +The evaluation results across the tested models are detailed below: -| Column | Description | -|---|---| -| `Molecule` | Original molecule name | -| `CHEBI_ID` | ID returned directly by ChEBI | -| `CHEBI_ID_from_KEGG` | ChEBI ID resolved via KEGG | -| `CHEBI_ID_from_PubChem` | ChEBI ID resolved via PubChem synonyms | -| `Match` | `True` if at least two sources agree | - -**`pubchem_comparaison_no_chebi_match.tsv`** — PubChem fallback for molecules with no ChEBI consensus: +| model_name | accuracy_percentage (%) | normalisation_times_sec (s) | normalisation_cost (USD/entity) | +|------------|------------------------:|----------------------------:|--------------------------------:| +| openai/gpt-5.5 | 99 | 1.65 | 0.0028 | +| qwen/qwen3.6-27b | 99 | 18.80 | 0.0012 | +| minimax/minimax-m2.7 | 99 | 10.72 | 0.0096 | +| anthropic/claude-opus-4.7 | 98 | 2.85 | 0.0013 | +| deepseek/deepseek-v4-pro | 97 | 8.39 | 0.0022 | +| openai/gpt-4o | 95 | 1.26 | 0.0016 | +| mistralai/mistral-large-2512 | 90 | 4.19 | 0.0001 | +| moonshotai/kimi-k2.6 | 89 | 28.17 | 0.0002 | +| google/gemma-4-31b-it | 62 | 2.44 | 0.0002 | -| Column | Description | -|---|---| -| `Molecule` | Original molecule name | -| `PubChem_ID` | ID returned directly by PubChem | -| `PubChem_ID_from_KEGG` | PubChem ID resolved via KEGG | -| `Match` | `True` if both sources agree | -### Normalize simulation times -Two scripts are involved: one evaluates candidate LLM models on a labelled gold standard, the other applies the selected model to the full dataset. +#### **Entity normalisation:** -**Model evaluation:** +To normalize simulation times, run: ```sh -uv run src/mdverse_entity_norm/scripts/normalize_simulation_time.py \ - --ground_truth_file data/STIME_ground_truth.json \ - --runs 10 \ - --model_evaluation_file results/norm_simu_times/model_evaluation.tsv +uv run src/mdverse_entity_norm/scripts/normalize_stime_results.py \ + --entities-file data/entities.tsv \ + --output-file results/norm_simu_times/normalized_stime_results.tsv ``` -This benchmarks 9 models via OpenRouter (including GPT-4o, DeepSeek V4 Pro, Claude Opus 4.7, and others) on a manually annotated gold standard of 100 simulation time entities, repeated over the specified number of runs. Results are saved to the file specified by `--model_evaluation_file`: +This applies DeepSeek V4 Pro to all STIME entities in the input file and writes a TSV with three columns: -| Column | Description | -|---|---| -| `model_name` | Model identifier | -| `accuracy_percentage` | Average accuracy across runs (%) | -| `normalisation_times_sec` | Average processing time per entity (s) | -| `normalisation_cost` | Average cost per entity (USD) | +| Column | Description | +| ------------- | ----------------------------------------------------------- | +| `STIME` | Original simulation time string | +| `LLM_value` | Normalised numeric value | +| `LLM_unit` | Normalised unit (`ps`, `ns`, `μs`, `ms`, or `s`) | -> An `OPEN_ROUTER_KEY` environment variable must be set (e.g. via a `.env` file) for API access. -**Entity normalisation:** +### Ground molecule names + +The grounding logic is illustrated below: + +![Grounding logic](plots/molecules_grounding_logic.png) ```sh -uv run src/mdverse_entity_norm/scripts/normalize_stime_results.py \ - --entities-file data/entities.tsv \ - --output-file results/norm_simu_times/normalized_stime_results.tsv +uv run src/mdverse_entity_norm/scripts/normalize_molecules.py ``` -This applies DeepSeek V4 Pro to all STIME entities in the input file and writes a TSV with three columns: +This reads molecular entities from `data/entities.tsv`. Entities are first classified by type (PDB, UniProt, DNA, RNA, protein, or small molecule). PDB and UniProt entries are resolved via their respective APIs and saved to `results/ground_molecule/same_grounding_mol/pdb_uniprot_seq_entities.tsv`. Small molecules are grounded by consensus across ChEBI, PubChem, and KEGG, producing two output files: + +**`chebi_comparaison.tsv`** — ChEBI grounding results for all small molecules: + +| Column | Description | +| ------------------------- | -------------------------------------- | +| `Molecule` | Original molecule name | +| `CHEBI_ID` | ID returned directly by ChEBI | +| `CHEBI_ID_from_KEGG` | ChEBI ID resolved via KEGG | +| `CHEBI_ID_from_PubChem` | ChEBI ID resolved via PubChem synonyms | +| `Match` | `True` if at least two sources agree | + +**`pubchem_comparaison_no_chebi_match.tsv`** — PubChem fallback for molecules with no ChEBI consensus: + +| Column | Description | +| ------------------------ | ------------------------------- | +| `Molecule` | Original molecule name | +| `PubChem_ID` | ID returned directly by PubChem | +| `PubChem_ID_from_KEGG` | PubChem ID resolved via KEGG | +| `Match` | `True` if both sources agree | + -| Column | Description | -|---|---| -| `STIME` | Original simulation time string | -| `LLM_value` | Normalised numeric value | -| `LLM_unit` | Normalised unit (`ps`, `ns`, `μs`, `ms`, or `s`) | From c48bc7151526f756248d910c358fae5dc4fb3364 Mon Sep 17 00:00:00 2001 From: essmaw Date: Mon, 6 Jul 2026 16:56:16 +0200 Subject: [PATCH 20/26] refactor: update temperature normalization script to use Click for CLI and improve file handling --- .../scripts/normalize_stemp.py | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/src/mdverse_entity_norm/scripts/normalize_stemp.py b/src/mdverse_entity_norm/scripts/normalize_stemp.py index 5f1541b..fb4437c 100644 --- a/src/mdverse_entity_norm/scripts/normalize_stemp.py +++ b/src/mdverse_entity_norm/scripts/normalize_stemp.py @@ -1,8 +1,9 @@ """Script to normalize simulation temperature entities.""" import re +from pathlib import Path -import matplotlib.pyplot as plt +import click import pandas as pd from loguru import logger @@ -68,19 +69,22 @@ def norm_temp(temp_str: str) -> tuple: return temperature_value, temperature_unit -def create_norm_temp_file(raw_temp_file: str, norm_temp_file: str) -> None: +def create_norm_temp_file(raw_temp_file: Path, norm_temp_file: Path) -> None: """Create a .tsv file containing the raw temperature value. the normalised temperature value and the normalised unit. Parameters ---------- - raw_temp_file (str) : name of the input file containing the raw values - norm_temp_file (str) : name of the input file with the normalised informations + raw_temp_file (Path) : path to the input file containing the raw values + norm_temp_file (Path) : path to the input file with the normalised informations """ df = pd.read_csv(raw_temp_file, sep="\t") - temp_entities = df[df["category"] == "TEMP"]["entity"].tolist() + temp_entities = df[df["category"] == "STEMP"]["entity"].tolist() + + if not norm_temp_file.parent.exists(): + norm_temp_file.parent.mkdir(parents=True, exist_ok=True) with open(norm_temp_file, "w") as f2: f2.write( @@ -99,30 +103,21 @@ def create_norm_temp_file(raw_temp_file: str, norm_temp_file: str) -> None: f2.write(f"{raw_temp}\tERROR\tERROR\tERROR\n") -def visualize_entity_count(file_path): - """Visualize the number of unique temperature entities before/after normalization. - - Parameters - ---------- - file_path (str): Path to the TSV file containing the normalized temperature results. - """ - temp_normalisation_results = pd.read_csv(file_path, sep="\t") - - before = len(temp_normalisation_results["raw_temperature"].unique()) - after = len(temp_normalisation_results["normalized_result"].unique()) - - plt.figure(figsize=(6, 5)) - - labels = ["Before Grounding", "After Grounding"] - values = [before, after] - - plt.bar(labels, values) - plt.ylabel("Number of unique temperatures") - plt.title("Unique Temperature Count") - plt.tight_layout() - plt.savefig("results/norm_temp/entity_count.png") +@click.command() +@click.option( + "--raw-entities-path", + type=click.Path(exists=True, dir_okay=False, path_type=Path), + help="Path to the input file containing raw temperature entities.", +) +@click.option( + "--normalized-stemp-path", + type=click.Path(dir_okay=False, path_type=Path), + help="Path to the output file for normalized temperature entities.", +) +def main(raw_entities_path: Path, normalized_stemp_path: Path): + """Normalize all the temperature entities in the input file and visualization.""" + create_norm_temp_file(raw_entities_path, normalized_stemp_path) if __name__ == "__main__": - create_norm_temp_file("data/entities.tsv", "results/norm_temp.tsv") - visualize_entity_count("results/norm_temp.tsv") + main() From 0d0b29bbf761c28918e62a1e86436edf039f5997 Mon Sep 17 00:00:00 2001 From: essmaw Date: Mon, 6 Jul 2026 17:14:36 +0200 Subject: [PATCH 21/26] refactor: improve README formatting and clarify entity normalization instructions. --- README.md | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index c77d19b..307be32 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ uv run src/mdverse_entity_norm/scripts/normalize_stemp.py --raw-entities-path da This reads temperature entities from `data/entities.tsv` and writes `results/STEMP/stemp_normalized.tsv`, a TSV file with four columns: -raw_temperature | normalised_temperature | normalised_unit | normalized_result -|---|---|---|---| -| 315 | 315 | K | 315 K -| 20°C | 293,15 | K | 293,15 K -| 310k | 310 | K | 310 K +| raw_temperature | normalised_temperature | normalised_unit | normalized_result | +| --------------- | ---------------------- | --------------- | ----------------- | +| 315 | 315 | K | 315 K | +| 20°C | 293,15 | K | 293,15 K | +| 310k | 310 | K | 310 K | > Special cases `room temperature` and `human body temperature` are normalised to 293 K and 310 K respectively. All Celsius values are converted to Kelvin. @@ -48,7 +48,7 @@ The normalization of simulation times is a two-step process: first, we benchmark > 🔑 An `OPEN_ROUTER_KEY` environment variable must be set (e.g., via a .env file) to authenticate and authorise API requests to the external LLM providers hosted on OpenRouter. -#### **Model evaluation:** +#### Model evaluation: To evaluate candidate LLM models on a labelled gold standard, run: @@ -64,23 +64,23 @@ This script benchmarks 9 models accessible via OpenRouter (including `GPT-4o`, ` The evaluation results across the tested models are detailed below: -| model_name | accuracy_percentage (%) | normalisation_times_sec (s) | normalisation_cost (USD/entity) | -|------------|------------------------:|----------------------------:|--------------------------------:| -| openai/gpt-5.5 | 99 | 1.65 | 0.0028 | -| qwen/qwen3.6-27b | 99 | 18.80 | 0.0012 | -| minimax/minimax-m2.7 | 99 | 10.72 | 0.0096 | -| anthropic/claude-opus-4.7 | 98 | 2.85 | 0.0013 | -| deepseek/deepseek-v4-pro | 97 | 8.39 | 0.0022 | -| openai/gpt-4o | 95 | 1.26 | 0.0016 | -| mistralai/mistral-large-2512 | 90 | 4.19 | 0.0001 | -| moonshotai/kimi-k2.6 | 89 | 28.17 | 0.0002 | -| google/gemma-4-31b-it | 62 | 2.44 | 0.0002 | +| model_name | accuracy_percentage (%) | normalisation_times_sec (s) | normalisation_cost (USD/entity) | +| ---------------------------------- | ----------------------: | --------------------------: | ------------------------------: | +| openai/gpt-5.5 | 99 | 1.65 | 0.0028 | +| qwen/qwen3.6-27b | 99 | 18.80 | 0.0012 | +| minimax/minimax-m2.7 | 99 | 10.72 | 0.0096 | +| anthropic/claude-opus-4.7 | 98 | 2.85 | 0.0013 | +| **deepseek/deepseek-v4-pro** | **97** | **8.39** | **0.0022** | +| openai/gpt-4o | 95 | 1.26 | 0.0016 | +| mistralai/mistral-large-2512 | 90 | 4.19 | 0.0001 | +| moonshotai/kimi-k2.6 | 89 | 28.17 | 0.0002 | +| google/gemma-4-31b-it | 62 | 2.44 | 0.0002 | +#### Entity normalization: +Based on these results, **DeepSeek V4 Pro** was selected as the optimal open-weight model, offering the best balance between high accuracy (97%), reasonable latency, and cost efficiency. -#### **Entity normalisation:** - -To normalize simulation times, run: +To apply this model and normalize the entire dataset, run: ```sh uv run src/mdverse_entity_norm/scripts/normalize_stime_results.py \ @@ -88,14 +88,14 @@ uv run src/mdverse_entity_norm/scripts/normalize_stime_results.py \ --output-file results/norm_simu_times/normalized_stime_results.tsv ``` -This applies DeepSeek V4 Pro to all STIME entities in the input file and writes a TSV with three columns: - -| Column | Description | -| ------------- | ----------------------------------------------------------- | -| `STIME` | Original simulation time string | -| `LLM_value` | Normalised numeric value | -| `LLM_unit` | Normalised unit (`ps`, `ns`, `μs`, `ms`, or `s`) | +This processes all raw STIME entities and outputs a three-column TSV with the standardized values and units: +| STIME | value | unit | +| ------------- | ----: | :--: | +| 1 μs | 1.0 | μs | +| 1 microsecond | 1.0 | μs | +| 200-300ns | 200.0 | ns | +| 200-300ns | 300.0 | ns | ### Ground molecule names @@ -127,5 +127,3 @@ This reads molecular entities from `data/entities.tsv`. Entities are first class | `PubChem_ID` | ID returned directly by PubChem | | `PubChem_ID_from_KEGG` | PubChem ID resolved via KEGG | | `Match` | `True` if both sources agree | - - From 7a0790e8095503bc7073214293354b18fcc6116f Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 17:18:37 +0200 Subject: [PATCH 22/26] refactor: add cli option and correct the code --- .../scripts/normalize_stime_wth_llm.py | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py b/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py index c56cb01..70533fb 100644 --- a/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py +++ b/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py @@ -10,9 +10,6 @@ normalize_simulation_time, ) -MODEL_NAME = "deepseek/deepseek-v4-pro" -PROMPT_PATH = Path("data/llm_prompt.txt") - UNITS_TO_NS = { "ps": 1e-3, "ns": 1, @@ -39,9 +36,7 @@ def get_stime_entities(entities_file: Path) -> pd.DataFrame: return stime_entities -def normalize_row( - raw_time, model_name: str = MODEL_NAME, prompt_path: Path = PROMPT_PATH -) -> str | None: +def normalize_row(raw_time, model_name: str, prompt_path: Path) -> str | None: """Normalize a single raw simulation time string using the LLM. Parameters @@ -66,21 +61,22 @@ def normalize_row( def normalize_dataframe_times( stime_entities: pd.DataFrame, + prompt_path: Path, + model_name: str, column: str = "entity", ) -> pd.DataFrame: """Apply LLM normalization to the specified column of the DataFrame. - Parameters - ---------- - stime_entities: DataFrame containing the entities to normalize. - column: The name of the column containing the raw simulation time strings. - Returns ------- pd.DataFrame: The input DataFrame with an additional column 'normalized_time' containing the normalized values. """ - stime_entities["normalized_time"] = stime_entities[column].apply(normalize_row) + stime_entities["normalized_time"] = stime_entities[column].apply( + lambda x: normalize_row( + raw_time=x, model_name=model_name, prompt_path=prompt_path + ) + ) return stime_entities @@ -136,29 +132,36 @@ def save_results_to_tsv(results: list, output_file: Path): @click.command() @click.option( - "--entities-file", + "--entities-path", type=click.Path(exists=True, dir_okay=False, path_type=Path), - default=Path("../data/ground_entities.tsv"), help="Path to the TSV file containing the entities.", ) @click.option( - "--output-file", + "--normalization-results-path", type=click.Path(dir_okay=False, path_type=Path), - default=Path("../results/norm_simu_times/normalized_stime_results.tsv"), help="Path to the output TSV file for normalized results.", ) -def main(entities_file: Path, output_file: Path): - """Execute the normalization process. - - Parameters - ---------- - entities_file: Path to the TSV file containing the entities. - output_file: Path to the output TSV file for normalized results. - """ - stime_entities = get_stime_entities(entities_file) - stime_entities = normalize_dataframe_times(stime_entities) +@click.option( + "--prompt-path", + type=click.Path(file_okay=True, path_type=Path), + help="Path to the llm prompt file", +) +@click.option( + "--model_name", + type=str, + help="Name of the LLM model to use for normalization.", +) +def main( + entities_path: Path, + normalization_results_path: Path, + prompt_path: Path, + model_name: str, +): + """Execute the normalization process.""" + stime_entities = get_stime_entities(entities_path) + stime_entities = normalize_dataframe_times(stime_entities, prompt_path, model_name) results = get_llm_normalization_results(stime_entities) - save_results_to_tsv(results, output_file) + save_results_to_tsv(results, normalization_results_path) if __name__ == "__main__": From 7ca60590f98eabac0dae68af5c361b6c9c8ef38d Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 17:20:01 +0200 Subject: [PATCH 23/26] chore: change "_" with "-" --- src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py b/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py index 70533fb..12b6e53 100644 --- a/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py +++ b/src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py @@ -147,7 +147,7 @@ def save_results_to_tsv(results: list, output_file: Path): help="Path to the llm prompt file", ) @click.option( - "--model_name", + "--model-name", type=str, help="Name of the LLM model to use for normalization.", ) From 6720a820bcd0480e1b331e26b38f2271ab6735ae Mon Sep 17 00:00:00 2001 From: essmaw Date: Mon, 6 Jul 2026 17:20:27 +0200 Subject: [PATCH 24/26] refactor: update STIME normalization command to use new model and parameters. --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 307be32..b1bb9b6 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ The evaluation results across the tested models are detailed below: | moonshotai/kimi-k2.6 | 89 | 28.17 | 0.0002 | | google/gemma-4-31b-it | 62 | 2.44 | 0.0002 | + #### Entity normalization: Based on these results, **DeepSeek V4 Pro** was selected as the optimal open-weight model, offering the best balance between high accuracy (97%), reasonable latency, and cost efficiency. @@ -83,9 +84,7 @@ Based on these results, **DeepSeek V4 Pro** was selected as the optimal open-wei To apply this model and normalize the entire dataset, run: ```sh -uv run src/mdverse_entity_norm/scripts/normalize_stime_results.py \ - --entities-file data/entities.tsv \ - --output-file results/norm_simu_times/normalized_stime_results.tsv +uv run src/mdverse_entity_norm/scripts/normalize_stime_wth_llm.py --entities-path data/entities.tsv --normalization-results-path results/STIME/stime_normalized.tsv --prompt-path data/llm_prompt.txt --model-name "deepseek/deepseek-v4-pro" ``` This processes all raw STIME entities and outputs a three-column TSV with the standardized values and units: From 9ca08dbaff96ddebfdf0796fdfdca769ec962bcc Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 17:22:17 +0200 Subject: [PATCH 25/26] chore: remove pytest configuration file as it's no longer needed --- pytest.toml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 pytest.toml diff --git a/pytest.toml b/pytest.toml deleted file mode 100644 index 6240b87..0000000 --- a/pytest.toml +++ /dev/null @@ -1,5 +0,0 @@ -[pytest] -addopts = ["--strict-markers"] -markers = [ - "network: network tests that require internet access and could be slow", -] From a3dfb093dcce3a7a84369d5f499025b0c96745c0 Mon Sep 17 00:00:00 2001 From: InesZenati Date: Mon, 6 Jul 2026 17:27:54 +0200 Subject: [PATCH 26/26] chore: rename result folders and update gitignore --- .gitignore | 4 ++-- .../{FFM_normalized/FFM_ground.json => FFM/FFM_grounded.json} | 0 results/{SOFTNAME_normalized => SOFTNAME}/amber/codemeta.json | 0 .../charmm-gui/codemeta.json | 0 .../espresso++/codemeta.json | 0 .../{SOFTNAME_normalized => SOFTNAME}/gromacs/codemeta.json | 0 .../{SOFTNAME_normalized => SOFTNAME}/lammps/codemeta.json | 0 .../{SOFTNAME_normalized => SOFTNAME}/lassohtp/codemeta.json | 0 .../{SOFTNAME_normalized => SOFTNAME}/plumed/codemeta.json | 0 .../{SOFTNAME_normalized => SOFTNAME}/probis/codemeta.json | 0 results/{SOFTNAME_normalized => SOFTNAME}/vmd/codemeta.json | 0 11 files changed, 2 insertions(+), 2 deletions(-) rename results/{FFM_normalized/FFM_ground.json => FFM/FFM_grounded.json} (100%) rename results/{SOFTNAME_normalized => SOFTNAME}/amber/codemeta.json (100%) rename results/{SOFTNAME_normalized => SOFTNAME}/charmm-gui/codemeta.json (100%) rename results/{SOFTNAME_normalized => SOFTNAME}/espresso++/codemeta.json (100%) rename results/{SOFTNAME_normalized => SOFTNAME}/gromacs/codemeta.json (100%) rename results/{SOFTNAME_normalized => SOFTNAME}/lammps/codemeta.json (100%) rename results/{SOFTNAME_normalized => SOFTNAME}/lassohtp/codemeta.json (100%) rename results/{SOFTNAME_normalized => SOFTNAME}/plumed/codemeta.json (100%) rename results/{SOFTNAME_normalized => SOFTNAME}/probis/codemeta.json (100%) rename results/{SOFTNAME_normalized => SOFTNAME}/vmd/codemeta.json (100%) diff --git a/.gitignore b/.gitignore index 9ebda89..4820502 100644 --- a/.gitignore +++ b/.gitignore @@ -214,5 +214,5 @@ __marimo__/ # Results folder results/* -!results/FFM_normalized -!results/SOFTNAME_normalized +!results/FFM +!results/SOFTNAME diff --git a/results/FFM_normalized/FFM_ground.json b/results/FFM/FFM_grounded.json similarity index 100% rename from results/FFM_normalized/FFM_ground.json rename to results/FFM/FFM_grounded.json diff --git a/results/SOFTNAME_normalized/amber/codemeta.json b/results/SOFTNAME/amber/codemeta.json similarity index 100% rename from results/SOFTNAME_normalized/amber/codemeta.json rename to results/SOFTNAME/amber/codemeta.json diff --git a/results/SOFTNAME_normalized/charmm-gui/codemeta.json b/results/SOFTNAME/charmm-gui/codemeta.json similarity index 100% rename from results/SOFTNAME_normalized/charmm-gui/codemeta.json rename to results/SOFTNAME/charmm-gui/codemeta.json diff --git a/results/SOFTNAME_normalized/espresso++/codemeta.json b/results/SOFTNAME/espresso++/codemeta.json similarity index 100% rename from results/SOFTNAME_normalized/espresso++/codemeta.json rename to results/SOFTNAME/espresso++/codemeta.json diff --git a/results/SOFTNAME_normalized/gromacs/codemeta.json b/results/SOFTNAME/gromacs/codemeta.json similarity index 100% rename from results/SOFTNAME_normalized/gromacs/codemeta.json rename to results/SOFTNAME/gromacs/codemeta.json diff --git a/results/SOFTNAME_normalized/lammps/codemeta.json b/results/SOFTNAME/lammps/codemeta.json similarity index 100% rename from results/SOFTNAME_normalized/lammps/codemeta.json rename to results/SOFTNAME/lammps/codemeta.json diff --git a/results/SOFTNAME_normalized/lassohtp/codemeta.json b/results/SOFTNAME/lassohtp/codemeta.json similarity index 100% rename from results/SOFTNAME_normalized/lassohtp/codemeta.json rename to results/SOFTNAME/lassohtp/codemeta.json diff --git a/results/SOFTNAME_normalized/plumed/codemeta.json b/results/SOFTNAME/plumed/codemeta.json similarity index 100% rename from results/SOFTNAME_normalized/plumed/codemeta.json rename to results/SOFTNAME/plumed/codemeta.json diff --git a/results/SOFTNAME_normalized/probis/codemeta.json b/results/SOFTNAME/probis/codemeta.json similarity index 100% rename from results/SOFTNAME_normalized/probis/codemeta.json rename to results/SOFTNAME/probis/codemeta.json diff --git a/results/SOFTNAME_normalized/vmd/codemeta.json b/results/SOFTNAME/vmd/codemeta.json similarity index 100% rename from results/SOFTNAME_normalized/vmd/codemeta.json rename to results/SOFTNAME/vmd/codemeta.json