From 32dcedc3c3c4f72130b6fe49558cafb9493a86f2 Mon Sep 17 00:00:00 2001 From: MarJMue <49639740+MarJMue@users.noreply.github.com> Date: Wed, 3 Jul 2024 19:19:02 +0200 Subject: [PATCH 1/6] Add a fitting benchmark --- tests/benchmark_fitting.py | 114 ++++++++++++++++++++ tests/benchmark_fitting/SiO2onSi.ellips.nxs | Bin 0 -> 120344 bytes 2 files changed, 114 insertions(+) create mode 100644 tests/benchmark_fitting.py create mode 100644 tests/benchmark_fitting/SiO2onSi.ellips.nxs diff --git a/tests/benchmark_fitting.py b/tests/benchmark_fitting.py new file mode 100644 index 00000000..fd24c0a3 --- /dev/null +++ b/tests/benchmark_fitting.py @@ -0,0 +1,114 @@ +"""Benchmark for using the formula dispersion""" + +import elli +import numpy as np +from elli.fitting import ParamsHist, fit +from fixtures import datadir +from pytest import fixture + + +def test_fitting_structure_creation(benchmark, datadir): + ANGLE = 70 + rii_db = elli.db.RII() + Si = rii_db.get_mat("Si", "Aspnes") + + psi_delta = ( + elli.read_nexus_psi_delta(datadir / "SiO2onSi.ellips.nxs") + .loc[ANGLE] + .loc[210:800] + ) + + params = ParamsHist() + params.add("SiO2_n0", value=1.6, min=-100, max=100, vary=True) + params.add("SiO2_n1", value=36, min=-40000, max=40000, vary=False) + params.add("SiO2_n2", value=0, min=-40000, max=40000, vary=False) + params.add("SiO2_k0", value=0, min=-100, max=100, vary=False) + params.add("SiO2_k1", value=0, min=-40000, max=40000, vary=False) + params.add("SiO2_k2", value=0, min=-40000, max=40000, vary=False) + params.add("SiO2_d", value=20, min=0, max=40000, vary=True) + + @fit(psi_delta, params) + def model(lbda, params): + SiO2 = elli.Cauchy( + params["SiO2_n0"], + params["SiO2_n1"], + params["SiO2_n2"], + params["SiO2_k0"], + params["SiO2_k1"], + params["SiO2_k2"], + ).get_mat() + + return elli.Structure( + elli.AIR, + [elli.Layer(SiO2, params["SiO2_d"])], + Si, + ).evaluate(lbda, ANGLE, solver=elli.Solver2x2) + + result = benchmark.pedantic( + model.fit, + args=(), + iterations=1, + rounds=10, + ) + assert result.chisqr < 0.02 + + +def test_fitting_structure_updates(benchmark, datadir): + ANGLE = 70 + rii_db = elli.db.RII() + Si = rii_db.get_mat("Si", "Aspnes") + + psi_delta = ( + elli.read_nexus_psi_delta(datadir / "SiO2onSi.ellips.nxs") + .loc[ANGLE] + .loc[210:800] + ) + + params = ParamsHist() + params.add("SiO2_n0", value=1.6, min=-100, max=100, vary=True) + params.add("SiO2_n1", value=36, min=-40000, max=40000, vary=False) + params.add("SiO2_n2", value=0, min=-40000, max=40000, vary=False) + params.add("SiO2_k0", value=0, min=-100, max=100, vary=False) + params.add("SiO2_k1", value=0, min=-40000, max=40000, vary=False) + params.add("SiO2_k2", value=0, min=-40000, max=40000, vary=False) + params.add("SiO2_d", value=20, min=0, max=40000, vary=True) + + SiO2 = elli.Cauchy( + params["SiO2_n0"], + params["SiO2_n1"], + params["SiO2_n2"], + params["SiO2_k0"], + params["SiO2_k1"], + params["SiO2_k2"], + ) + + SiO2_mat = SiO2.get_mat() + + layer = elli.Layer(SiO2_mat, params["SiO2_d"]) + + structure = elli.Structure( + elli.AIR, + [layer], + Si, + ) + + @fit(psi_delta, params) + def model(lbda, params): + SiO2.single_params["n0"] = params["SiO2_n0"] + SiO2.single_params["n1"] = params["SiO2_n1"] + SiO2.single_params["n2"] = params["SiO2_n2"] + SiO2.single_params["k0"] = params["SiO2_k0"] + SiO2.single_params["k1"] = params["SiO2_k1"] + SiO2.single_params["k2"] = params["SiO2_k2"] + + layer.set_thickness(params["SiO2_d"]) + + return structure.evaluate(lbda, ANGLE, solver=elli.Solver2x2) + + result = benchmark.pedantic( + model.fit, + args=(), + iterations=1, + rounds=10, + ) + assert result.chisqr < 0.02 diff --git a/tests/benchmark_fitting/SiO2onSi.ellips.nxs b/tests/benchmark_fitting/SiO2onSi.ellips.nxs new file mode 100644 index 0000000000000000000000000000000000000000..a44b6be4a339f06dedc3a574bee5ea49f858ef28 GIT binary patch literal 120344 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%0A`7&sLr+X2o4+uo}qOadx zWNMPV=v3vKCvSK5qHiNjO(!(>lKYc3@RIwZdwEg7xKkv&Q1R^6P&mh~d+s^;4E zuc!2hKlG$1zv10>?e?UM%Wp!SEcT>`$st9(Qaq`&*xw&z)8PtNbP2ooct~)A&M&JH0vO zSWwB+okV!_nkw#OcdKWG;YFd}XLg$X&5cIpZ3^+rbEDmLMxM95?nX!64SX?iuNzhW zF{yXf8aK*1wa_tXx*NR;!XJ8|8+8y9O3;z7-d<|d&W$3)1d)%M{S{?$+|`wC%)Rs}K#la_ zkGs27cO_w$81UUi@+;oDP$yg4;Z^Rr$bBr%xKQ7@P8A0qa3P1yKgJzf>q2WU+3nl1 z+=W_y+_$d!Oc%)qOL3vC+b{t+0BOf5T@&M7Xzb&UZ#%bhAw)5stm5ZF*2`Bm%yV~< z=dE%f&p$0f7glzWT(k-r6Ac&F?dEh%<@s>v6rEse}kv zXzxr2vsf|8isKY?U0lcCnX(aH(aOV_tYtE+TkM_bWbXN`c{QDxBiy32GX-sMj!yjJ zL}DAs?gdV?sonG8jXyb2jA%fgI?-XzdhL5$bE0SNVrIpOuzOHVDOTjR}MBfB|K zT8mRlx|0h z&#zW=qP`+Pp@b8iYJri{4@cQ=eCJ3rT7A-`Ja?of(*~Ao{LqoA_iXif+BHX-^Gh+Q zSdJr2Nk9v9$dNkYwX$xvBd@#t-i*(QIPPVRbQasKcrJFN#WOAMv~wJ3_OtzmD~xxf zsaLR}`4C6KTUZtIpCiGfxBXieN2>a=%FtO+jwD{gicXG{yQ9qFqOBcC*ZsWx>qd^G zx_hcjioYXO#&&40>N`?rgaI~pbtG-A{z-v4M~V;wuiB2}ext;p^A#QGLlxW0@5?$; z`5Z-`Pd1M9XZ)9Yw+bBSxQD7)v-b|vw%4NMC9fSQs~uLbUN}e}=R*g%&UeN^?w@@E z^*_D1mzw23?=If`uf_of>95;`@}Sj~d^S3eSawaya3IJ3HoTiT7sp-QwZg7h4&*0{ zG2S$=SO>T6brX6?MdMKF6P;j>&#%! zm5=SEzw*941w4GV_vLkaTKOtJq3?Nn$@RQoPjGR0T{~${Men;uyYI56a<4vAJh>Mt z#;g17>Fc37Wy@@|rzV5UGrq1uJ#2@Zx740iEL(reW0pN#89KFdCe+g>Y0lT#_LRGL z%_Y0h_Oulk!@i^JCC?G*u~h@oqlVhkpoo@94SL&C{ipw}zSY&9diDzTvV%5qkGmZk zWls$!uWouc(q8WW+}57vCMc$DX>L#0t-5HHruH9R24{PU@vpf(L~Tz#KdM=v^*+PM9?j&#-Flsm7Z5)Y!U*38k7 zVR-hLmeqovs1=vb7FTDCd9cUuP?MULKc zV^cdFO;fo}UC>NN!j4|DsZRR+gLE`*>W&m2KOJ42Rwu=%*HL{<-mDFQ_+2wg|InLt z6xd~Fl)+U;w=342-%qEbc{|GW+vKLBqCeJ#P1NGL`GBaT!S`dCV`(j&jLR*nBT!eS z?kK4v-k;i5N86?j*|E5^j!Mmb^0Z1Bom`jrrjbE?}eU$IwoZ$)WA3ge$F+2&VRp`>V_H)FS9g2$Q(8KCe|Or5qguLJnlz_u~JL9*rs#Q3N4kqvtvZ(3@zy~9rJp&R{9lYXk{GyR4qOJx^QQMbS*Wo zUYOxJNlO(kRZb|LrltNN9h2Qtpun+q9jKN4L#Vs!lYz4aX{Dd9x0VKoz=piv9UQ9GbYe_86eu>nQu&Z|r#q|`1wDwvOb|(Y$Oap-9kuSDizTHMk zKX*;~dpTH3RU>*i?{1={srk*%HEpGp{SLzQn|12iCx5Nvi5j(Xe2e_=&(9A|H)tsz zcEPlIS{mBG=Jf?EP0xVbu*U2HU+~(?9%G#*(s!&Typ#FS)Uq&n2dnk6}`!%Qnu6r%)s9j!5troOMski8 zYG`#T0y0+My21AEzeqzz#sKU)TSK4-H(!8qjvJn*p>s#(b=)#tBj+_|YN(?C$Bfrd zeN2xSMrr83+V^@{ke*Wy9mFvj*`B9psOQAFS57BuXn_xQVCakAQ5_K^LvTLPAhh(+ zP{V0?r_Z7sG{B3$`)lMl5%mOyIs{t910?@$8agyPWcq(SG<0JAMD42H8ZsSdo_q^w z(69QPh||#h7PVInO3)Cf6}!$vXlV4}oXP328WMK&*_||0Bp}l|sD(!I)!Jw%Ik85$ zEvN@5L`Gmo4gJdd`?_BfoR`DtQtL=VQ8PTw-3ruD=Gz4a-0(Sp&8QNrp@6aD{+4cl zayn+rb9M|p_oa2hmnzY8x7T^R_FLw%f|LtaEPAN;Pecmvkc zkZ*>P zYvXzzoioM;y2%Jfb{!2J?tu-^bU04VKSkSG8p#o^qMzG>)ezj?_wY&2B9*7VTQ|J1TwE{5N) zD!=ypS2dxRU!lxTH5GGiJnwvgTFyIuR@2kAuDPXA&ihh*Qd6T!p?i0}SJT*gcaMF3 zt(Lz7%Dm>6YBGilI(-4EyuWn!y>~c{*bwXu>I0X0<%OC`*Z5Sb_cJxcwym^wHng4@ zk>a=1^ytsW=-p>=9z(J7)MYiT>kVi8BQ?GFwf^JP=W4QA=XiF{Rnwr&St0GOsO9+k zqM9;3R9^BE_1&tzN@;yZO1q-`T5cD@*`rbSnOb}EK?ql2qB$Bj|buv$6& zTj29K?WX(g8>yyjF-O$e86JPY-IVw>b1eQ}=Rc}`XnXd;#cz0~A@Z7YM~)pQWu zf^4YhF5XX4)7R9H0iXJ-sjC3@#;R$8-5A}NXf=J0NL;h4gPM}j{0<6NQ@Ry^qt0r{ zHADUg0Xhp+Q?-s@OPwNgE81+^~tGf-G zxIJG*bwq%~YZXP`yXSExR|Rint=b{iRrKrm>}tnzRMZ+oT{R`XC6BbO;^#ta;Kx;r>JO~X-UD25i0q=8>XVj>rVTR4_48X{Q!OTRS_ur z`j@>_bZ&puj&uK0Nv=RQ6=BveuymA)oR>EBx&hs3!HA-hiUi*BdIuHnvlHK1MY;Q? zEUw*DMT1Xh>NIVlqVH!9wZCOiaXjlAvr5Ks`l@I__Zg{cy;Nl0y1hl1r;3)`OB)nkw30oUfww+ zjp~LT3ly`X?v0KqNnp!b99EL`owj9Ls2EQDJD{WoG$#4eex)3*98}W5`M;hyZ&Ol_ z+Ep*utW#3!Yu#+4)+*_S#(2Cu()BUT5`nZbd+HpIOeMAYQsAu1P*TYiho-MwsHFSA ziqx2?q`S2%O&L2G-z&y<6O<&T8z)c1@65EfzdBAS*Ox{sCC?)TpKo$zCB?@_H>qn?5~A>~u5PH5{m3AtnQ1K6A*T4DXFvAD8K=~7u!47qaIdycDJdlB!PJ;T@lYm1zlQ`vPxQ+xA9rLty1nU zQ(j3mFY3=_=$oJw^-le zWUfN`h8`(scMY)ute}c7Tiy+Wrf;j=BK)3$1{d5M|MjLq-k&=Pj>ihRt)PW=OEZ$M z;rsrb9{BRQLh|jeD%b}dc22?jhen)LP>{+!KIoK!T00`r?5KkJ4V?RK%W-`Fvf7>x zPAJG%bfAtYDCG6ItF5yXIS!7duz%B)e?Sk%XRnYT20PAhU@yl&) zeQ}$DB3dJ2c)LRK+14xQeS0@-9iyNyB;Avt&B6*Mre!rSf{LrgR$B`_aCg$5#AF5SZCw26D6`*o8AhFnttr7bCQD8!S$crM!nizO@Ho!ULKB(VNkyAc$vK; z5)^cC^}Y@HofK4AYwgtlWk7lB8-Y9a zH&;-}iVfy;Z>Es*V<@kszEXTrQ`EcLJGBqWnd5=uoW8t$Y;%;0=l6DKXgM-@5TzEdp&Q~Lj43)LWCE}+XEwf*MQ@T{dDUqC{BQ!Q6J$Y)!p?9 zI@|L=#!FY+xAk3bhN>0h)eU#r8|O9Z$N~>P{BJw~hV@dA+oX;W_rer3`?wR{U<&Dz zyi-@kqp$8D;P~|>S35`_|LYENKiz*F2)&xW^Xhbz_t&qZj5iDGNMf0{SMQD# zw7JK_e$zTq8IL!Aw{Pqy*BeiCq=}aRqIlF%#uxnRNL^PBNSRbKT;6xDaLFle5-xdz zUBYEt-0*O^U9PWX&zx|{&)6I;<8e-hOW*IaaONkt{tcHrzZwx#d;HJNw>=}MBdy42 z&=hG58{c=0;C-&9jfo)L?R|ecXGF;Tc@ISJKDEv6L~uOn<{uF^1i8B*OJRlypfQuJ$?n-c3S5QGt z1qA}i4T=aT>Pmvj;=YH(eRPUiH%v`2r`O!m;GxAS>O1~h zDQeyGMT)r>pI;uVI@tW+vU{z>YX_U3{h{XTv%3#gydxt9n~Wzmaj+@dPFE}(tm1j? z8m#Q&jtn*#CoFb|S$+DUhI4KnV#Y0Lu&QU~5cA{cdq>xuF+|-@8-~y?aKpDl%$ZkT zx95&4hbp_S0YjBt?)agKM|b5=Q|R)4{C=o8vVZH&JDa(bU5ndgc0ibyic?+6ZtG2# zStsCaaP*i<+2hx9D?iLuZpKw@beo%eAUnNo#XobeoA#>llig-Y&ZeSI=ecQ@J9E98 zejE)7-6lU|9(Aj@p%+=o@2Z)l>{h#4^ykh_woHlN_VJyTif5l=DV~Rik&m$C9iOnw z8p+jNf16>M?5Y20F^+1JgO=I-n7eTKx0d+_xHoqH$zr_0`;S?)+u0Q5 zQGQb~9_0s6)x&s;Q!ex<|Kr*o73aR5$NYKk{`@Mj9_3He&}07P?h)=6uJI`U(&iq; zmy+mV9Jp<5Jj(99qesPM?&4AYO+7p+URZCBiLb#$3;KJMpTi)O6T(lAhjy2H!7tN( z+HJXe10LGB-Z{#n+CSRE=gjB{9{Nw+`jki6b=a)`yAYrK`Yj&wi|noD zzK=Z0e)3xn?ZIxTno2*Nstr@k>xEeM`cyM%Kz#n~;F%d6*pc^3D3x)ucRikJuIcb- z?DO+dIqnv2N~OQtuKlUBzaLT2%Q&Oe>U&kZdGMRiAF7W^|D)|y{(U)KW$*Z?*Su#~ zkK7q~US%H){t?%nNMI?_<+nd~I#;UC4)$ zdf=}R9-T{V6>qn`ZRX+~{NXFWOM-aOuf%}|qpHg_q7nEwYJ58Dp~lb?JHxANW$z1_ z_Pot)#XHdw^$XRE#1l^HWGlbc&fqT*Khlnx z?*wvvA&Bb=UJux#6?L)cud)XyW@Zlo>Cb+XZ6dl(RL-ro8Qr{*I}H3`Q{UVtK%PW;sA)ap`GGchFqYh5tNV8l`a|L)Cfmx6Dg}HX?|hh7b%?F}vj?Le zU{2CtsBKCdeJ1;=<=>zL{)W#r3`i*Gc>v zfz>6D>u6iWyS&#{{`t^noa6g&KHZ1Ay~yYIflRw~=yBcvGEOM+WnB5kVBCQ>VHJ>m zM?mrc0LhbWEkqH+_dv)Xw9s5+?P9Xj1P@X&< z&}Upl$l`Z-6!|h9Vo;9#H&OEmA)RqXp-+2$q}Q3aG=DMV#b5Pshk*1ieH8VGVcruq z{dOLO%(&1%@k`$In63EDAPXPG1RRHPvLOra(XJ=e{oD;G`|z2QY%^_huTFD-^pgeB z{}TCPK6pKlcJY(ZUx-&d=qX#-Geb}O;r0MIUm~A=m_XX+LyvwMkYzjVZ$QS6oN6n6 zwCT38pO}X0FZQ=nZ2EmHgdXFo%>Xad(U;wGp~pC+K-$?Oo%bbB;%)r|lz6r=f3}t1 zDrDg!_yGFSf7c;h;z4c#@_HbjJSf2ES{HR%|0i3`6F|;0Kt5-m*3U1X+_!D=Y&8!f z|Hdtc3lE?^*={(D{OyYl#8w5;p9?7Qa$lLL`HE1Eagl-K1A9i_KgbvV&XGWwH`*ed zc8-5RzaoCe7qjsEy$@fKkzN;OTz!!)ybf-=Qz#astxd-O#0A z$X{`t;ZMB``SkaKEYHIczz$Dt8Pgrec!lH`t-kl&Q|etkuK|n9Y~)Y{@nE>mAsnCm2xNR# z=#y^%di2u*N*v|6^KHd*hWtZIeqOx`$axd_5^wbvh!iH`*`e<{_kuUvh0A`6U+P zIfbxL&6i+3ApGsn6aScIsE7VGkm>&oJ$c^cA)R)+DF52|O*+j*K6wFgoR#%jyPkzC zd=5=fpZIh4U5fVIvC};RNjt>93iZkL zT?9S)9Yc?PIKYlGr^GIU{;_{9a{chCo@ash8fB&-o$oo&6aUWcuYi{ip0@PmCR zvF|QZ{BKVzx0OBbYqsLiM0xU7Af4@p9{rM#Fa9=5fz?NUZ2X9HKF1*o-`cyNBo%YUWsv3P%F0x>8(H% z<6ed92!G6RkY!w6vl{IcKQ-v_dH@->9eTpgG9Jk534QvrL*{)Cng01e@hkEny?QD< zEr7g_)?hr&ez9OH>Zd;g^n@4SZ6J9+URU$;-O!Wx)7v0RT-kX*@+Z9kUOL26eji9) zp0zliKRuG40a@;|%0T)xZyq^HD$JBB1>8dj8e>f%MaW9(kyce>WUDP3Q}M*yWH{Tydn}7U-*&-Eh8fhD`J^;VnwGmjyie5^q7 zQJ|bW2j{+peh^-!ZFsIvnr-xj{@zA!#_m8muLn^4Y`eUT`{i&e_v1K@@EYuZKK%xv z&-Eg3!A1m3L;e#7LG%t}{CT9)KMY8IBh)AU>MsLjoz)w9^kYT7_+j0Gc44V` z^#f4i+gu2gdGgX7c%I0*d#Bt2uYd>$a5@12klH7u{kJ9?e*o*I8Q@5VY< z`1g>`_pn_!PhbB+FY?J7gL33u#Cb3-DP;N;LQi<92B19cSKh_>!q4*tl%t+eHKKUPjy0_<3iqPC@H@l-t95FZ*8%zDKLIW+?B-g7c991I_0f;;LtF8W zp*+`#NSFPWtD#RnTJ!_`_20+*i|^jcrCo9zfb{eIJNg~GB5^41U9_xdBlN@%^1+XA zKV0@+{s^fb{zE>r$9l9uep~31Cj$D6dyf2JAcS8Aq#q*6F(38t`LoAX{<6sT*R8xJ z73IhW0zIyyphw;5eFzXA1;4-GP(myr1$1ex;_>Jgsa zSvWrVdU0IxSD+s9qadAe&!I=2HsB?B2+InTcq5P{$tKR&X<^lZ&=Ebfn3wNL% zt|xJx~t)qcb$Xs5g)_$rl2de3@tm zd8iKHdLw>(r9=9C3hBZJGY$IWhrx9qAIn#m|B}0nNjs?D7a;RK0E+*40QHg=1LbAk zGz-XmiLb$X+qmoMhB#mD)8Kq$AMG{Nf793fS9e5txet8EC*LfNC;rh#QJ#L|IIp9x zRxA1(<;eeq^IfpzVBUdmZ1SQd|DgG4A#;Ag@yHX1^OyamI)^b2fj^0JRM z563014)BG^=jHcD`CHC!mfHm9Cw~8RA@{p!urUMWWFA|Jbn;^W`MwAgp3glv&P$6M z7WG6w^8F9xcwZx*&qtv6gTICTpA_tezVO^X2BiNQko#@O=RO;-a-9MB?*N78<8>hS?@-_Pg+24v z{;c-}f5Ckn^G5Eo$1v~pPc(J{$Vcv(cz6Y`#^f<2>;LE}L&V5meZ)Ir(_-z+W%Kr(-{WKu? zuYkhK^%C^yF99T9H_FLoSHUWMjB?&AT;+a9C&Mu2QTkl#;$w3`RY`g{xO;XVk*`L9}dXp2~g(eSAfFj z*z7!gJRtY!kY0Dvv7r-ye9uMtjUZrHeZJzm`Tu#NIt)6nkVc6&1(iEFD`KBj++asU#R;Z zvg`*Bg3NigI&fy?XI4P2g)p@9f!sd<%D(BHK-pJx1LgfD8v2QY{yxSFEExae zf#jV6a=!}s!gF&B`h35I%y9|idlmFJp8|z{cN>uJhZm{iTm)I-&qYC&{km%|#<&%p zZ^(RZ0vTtij^^utJRC$0c|h`KAf0>^StZo@d6}&DUkebz#CTVUmb)V`DuajUONWJbqtXF zHPAz}oZRt1;ho(Em z#lSJ?PsZK_d}r>pc{cQ9zoie5yoN~UeR7!^uW3NJzdl1gd5a;F=dM1^|B1=2=*z)l z@xl7M#*l^A>>K2B-h@oP8OR(zK=MB!oxE~D@|U4Jd8mQp5kh))w}bhUAq&s(GYv3) z9=JVrBV>7wnUMMYGgk5Q6hP)W2l?c`L3!@)BmG#_N>PbG&X33^ee@)>zBgov8~voT=lf{aG~oOd zpN=_%bY2hWb&&=`=6D8*f7E;+_lb}%&y#N;lYj9F#ZULZl^Cz`K8JMjlL5s|D-QWw zPdCE2mHkNQIrZ>88ubZ(O>^M#V?*-VqdnXYz;)$3fb#X142*rOvF71y0)ASFX90ck z2BDwGBZuo%%`lBh*ei@OqK%7;7$eK?3x^=ri;6O80Oem4{u#)VpZdRHR1-lZ8KdR@ zMqU0b?T^&Yvbut(67^L?dC4m6xRs$V`4uXtYGU;9Dn@m_MjcaHTKqc`=atLq`V?0h zf&W?IK!z)vlW9w*yipkHZy2vT0u*+CCRDI z2w54K>9#A)7YYZ0Iff%k`*_5#FT-|a27G>wn&R;>PPgZO3vTb#yQ_?`?z#a-7L?tR zB%Qr%y84eErbf&nB{yE8FjMQzS*!n33gqhaW$Q{&(8-T$Oqk>x@znZrbn<6Y%GO(^ z^^Qy}TW_P*i_s;QZaOO6QK1JT44YSgF} zcUq$Yf%J4MqeW^UBi>F=_hp6x8Fo0B(>xRy79M2dxyx-2w3c0-YFdr6zvUG?K2W>lknB@zKu4)8& z$sZ2-JXv8`KoslZIMsE#oa>m-BB4bh()92;-d`oJTTNZgIq#DFSgV};+B(0X#(rHA z<3g#HU&iIhV>Hg^kHgt$y^G4Jw~o$t#+{v=*}Mntgp125cS(tIy^^v*SgxILe=3Cm z{;(a4!+I{Rr_T?lS5Cc`>U?M1bsrG-KXx$P=hx|%l~caH&UgA-7IJ1WRBOE9fi?V@u|jc#g!ClkZ%|(#FFT<&?j&ocu;Q-?^^6lGH-@DxL1! zpI$p13-C4xUVAumu0Cac+vE9q(mH;BtMaXn=Qq~no$Hqw^0|sj8%C2;EqFq-N7M>HOvcv=|ocmf1-;?;^j{^S<~dF(q=LjwLU zx3?pgtjUoDMW+*1VjzC$>W$ z;dtAAO}p=X@7;DR7hg)drL*LBhTDB&f2so%_wlc3_xk!>)t%K+mv-h&r@!TyEx$9| zZdK{2|NfeGKfiC~`z(E(uA^kTTj}&O+-_&Ap4f6H?bo#X#jo1l!LqGOX?L5*v|GkW z$)A(icmp zoxN1rcU}Y zf^!M~)jd$!_nRY&osp|f-)mJ~yVMy0C->Vqsq7wz+_+j3v2mro zVMS(KMXo=#N31{8x4FplM|taqxK--DbM{5c9&P7x|J!=NSwAe$C+3X8Wosk*Ja*>e zBg@rs>aI}ud*|a5*AMEOc8ybBvdBFr_D1YEp}tKxSLl?d^_vsbd>nmzpP zt7-XV>&597Wv?z<&-uaeo%4pWp>#7y+omm+1I?nVBA7i$Y;57i@mJ&iXl9H|?`!PRmc-))<{Ur;7TIqy4u29B@z@&OWa7>MlK5ujG5vEUowW zNj@d8qb{*OtE)FQ4m!EMB;CQ;&#QE&zhibeEz<{xFjO;U>HXtc*gvjoSXtTfybtEM zLcZaCE8Vf1FZDYM`~c-A-&y46;EDURVzYXp+!(TaD|6O=vd}(JzN&;QHnqj;%*x#7 zwz8G~TdD0r8TpAFtaBW(jV-RH3hPm4S-M}Lb2UzE|Fu%_aZ7Cq|Mn-!sDeFt{B#9T zT0ctnmnoa}_&MnKH`a{nq~8fmqg7R-Rgrd>DE4oxz#3!p_kcR?)3P@vXXJN~y`YZ<5|BTTjN4{L;3St>?5p z`|Yyzx~uApaofw*OVWDN-YHuzP3t-4Gmdd(woX4?_|K0kWmz4Gaiwgcohx&?J)!jf z^Zk*)14p7il&$sI)*s3eJQDq(Y`D+1{!kX?k?0R)8-BL+hq7FcM1Lro^WWDW&Qs@1 zum4N>?UeMx(ato*uLtp=OgmL9<7JT_UsBy!Mi|SS2A?Gux&6q_dg`xnQcwKW^^W)z OIO`qdYY>_Jm45-bpBH}s literal 0 HcmV?d00001 From e8a03ab2bda8fe2e17a02ef31c551dc6ee59ca11 Mon Sep 17 00:00:00 2001 From: MarJMue <49639740+MarJMue@users.noreply.github.com> Date: Wed, 3 Jul 2024 19:35:21 +0200 Subject: [PATCH 2/6] Make deepcopy in solver optional --- src/elli/solver.py | 7 +++++-- src/elli/solver4x4.py | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/elli/solver.py b/src/elli/solver.py index e0e8d48a..4f3ada49 100644 --- a/src/elli/solver.py +++ b/src/elli/solver.py @@ -26,8 +26,11 @@ class Solver(ABC): def calculate(self) -> Result: pass - def __init__(self, experiment: "Experiment") -> None: - self.experiment = deepcopy(experiment) + def __init__(self, experiment: "Experiment", save_experiment: bool = False) -> None: + if save_experiment: + self.experiment = deepcopy(experiment) + else: + self.experiment = experiment self.structure = self.experiment.structure self.lbda = self.experiment.lbda self.theta_i = self.experiment.theta_i diff --git a/src/elli/solver4x4.py b/src/elli/solver4x4.py index 6c3a10cd..8f57e22a 100644 --- a/src/elli/solver4x4.py +++ b/src/elli/solver4x4.py @@ -314,9 +314,12 @@ def get_k_z( return sqrt(k_z2) def __init__( - self, experiment: "Experiment", propagator: Propagator = PropagatorExpm() + self, + experiment: "Experiment", + propagator: Propagator = PropagatorExpm(), + save_experiment: bool = False, ) -> None: - super().__init__(experiment) + super().__init__(experiment, save_experiment) self.propagator = propagator def calculate(self) -> Result: From d306edd51c2f515ea4cc67f0af4bd98b3a8d57db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20M=C3=BCller?= <49639740+MarJMue@users.noreply.github.com> Date: Thu, 18 Jul 2024 14:19:17 +0200 Subject: [PATCH 3/6] Add simple caching for dispersions --- src/elli/dispersions/base_dispersion.py | 50 +++++++++++++++++++++++++ tests/benchmark_fitting.py | 12 +++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/elli/dispersions/base_dispersion.py b/src/elli/dispersions/base_dispersion.py index baa2a36f..22ea9846 100644 --- a/src/elli/dispersions/base_dispersion.py +++ b/src/elli/dispersions/base_dispersion.py @@ -8,6 +8,7 @@ import numpy as np import numpy.typing as npt import pandas as pd +from lmfit import Parameter from numpy.lib.scimath import sqrt from .. import dispersions @@ -44,6 +45,14 @@ def _guard_invalid_params(params1, params2): missing_param_strings = ", ".join(f"{p}" for p in missing_params) raise InvalidParameters(f"Invalid parameter(s): {missing_param_strings}") + @staticmethod + def _hash_params(params: dict | list[dict]) -> int: + """Creates an single_params_dict or the repeating_params_list.""" + if isinstance(params, list): + return hash(tuple([self._hash_params(dictionary) for dictionary in params])) + else: + return hash(tuple([item for _, item in params.items()])) + @staticmethod def _fill_params_dict(template: dict, *args, **kwargs) -> dict: BaseDispersion._guard_invalid_params(list(kwargs.keys()), list(template.keys())) @@ -56,6 +65,8 @@ def _fill_params_dict(template: dict, *args, **kwargs) -> dict: for i, val in enumerate(args): key = list(template.keys())[i] + if isinstance(val, Parameter): + val = val.value params[key] = val pos_arguments.add(key) @@ -64,6 +75,8 @@ def _fill_params_dict(template: dict, *args, **kwargs) -> dict: raise InvalidParameters( f"Parameter {key} already set by positional argument" ) + if isinstance(value, Parameter): + value = value.value params[key] = value return params @@ -80,6 +93,10 @@ def __init__(self, *args, **kwargs): if self.single_params[param] is None: raise InvalidParameters(f"Please specify parameter {param}") + self.last_lbda = None + self.hash_single_params = None + self.hash_rep_params = None + @abstractmethod def dielectric_function(self, lbda: npt.ArrayLike) -> npt.NDArray: """Calculates the dielectric function in a given wavelength window. @@ -114,6 +131,39 @@ def get_dielectric(self, lbda: Optional[npt.ArrayLike] = None) -> npt.NDArray: """Returns the dielectric constant for wavelength 'lbda' default unit (nm) in the convention ε1 + iε2.""" lbda = self.default_lbda_range if lbda is None else lbda + + from .table_epsilon import TableEpsilon + from .table_index import Table + + if not isinstance(self, (DispersionSum, IndexDispersionSum)): + if isinstance(self, (TableEpsilon, Table)): + if self.last_lbda is lbda: + return self.cached_diel + else: + self.last_lbda = lbda + self.cached_diel = np.asarray( + self.dielectric_function(lbda), dtype=np.complex128 + ) + return self.cached_diel + else: + new_single_hash = self._hash_params(self.single_params) + new_rep_hash = self._hash_params(self.rep_params) + + if ( + self.last_lbda is lbda + and self.hash_single_params == new_single_hash + and self.hash_rep_params == new_rep_hash + ): + return self.cached_diel + else: + self.last_lbda = lbda + self.hash_single_params = new_single_hash + self.hash_rep_params = new_rep_hash + self.cached_diel = np.asarray( + self.dielectric_function(lbda), dtype=np.complex128 + ) + return self.cached_diel + return np.asarray(self.dielectric_function(lbda), dtype=np.complex128) def get_refractive_index(self, lbda: Optional[npt.ArrayLike] = None) -> npt.NDArray: diff --git a/tests/benchmark_fitting.py b/tests/benchmark_fitting.py index fd24c0a3..8d924131 100644 --- a/tests/benchmark_fitting.py +++ b/tests/benchmark_fitting.py @@ -94,12 +94,12 @@ def test_fitting_structure_updates(benchmark, datadir): @fit(psi_delta, params) def model(lbda, params): - SiO2.single_params["n0"] = params["SiO2_n0"] - SiO2.single_params["n1"] = params["SiO2_n1"] - SiO2.single_params["n2"] = params["SiO2_n2"] - SiO2.single_params["k0"] = params["SiO2_k0"] - SiO2.single_params["k1"] = params["SiO2_k1"] - SiO2.single_params["k2"] = params["SiO2_k2"] + SiO2.single_params["n0"] = params["SiO2_n0"].value + SiO2.single_params["n1"] = params["SiO2_n1"].value + SiO2.single_params["n2"] = params["SiO2_n2"].value + SiO2.single_params["k0"] = params["SiO2_k0"].value + SiO2.single_params["k1"] = params["SiO2_k1"].value + SiO2.single_params["k2"] = params["SiO2_k2"].value layer.set_thickness(params["SiO2_d"]) From 8e216cfbace04e60ffc5dbdc6138d9a5590dc83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20M=C3=BCller?= <49639740+MarJMue@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:33:13 +0200 Subject: [PATCH 4/6] Fix errors --- src/elli/dispersions/base_dispersion.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/elli/dispersions/base_dispersion.py b/src/elli/dispersions/base_dispersion.py index 22ea9846..40768a20 100644 --- a/src/elli/dispersions/base_dispersion.py +++ b/src/elli/dispersions/base_dispersion.py @@ -45,14 +45,6 @@ def _guard_invalid_params(params1, params2): missing_param_strings = ", ".join(f"{p}" for p in missing_params) raise InvalidParameters(f"Invalid parameter(s): {missing_param_strings}") - @staticmethod - def _hash_params(params: dict | list[dict]) -> int: - """Creates an single_params_dict or the repeating_params_list.""" - if isinstance(params, list): - return hash(tuple([self._hash_params(dictionary) for dictionary in params])) - else: - return hash(tuple([item for _, item in params.items()])) - @staticmethod def _fill_params_dict(template: dict, *args, **kwargs) -> dict: BaseDispersion._guard_invalid_params(list(kwargs.keys()), list(template.keys())) @@ -97,6 +89,13 @@ def __init__(self, *args, **kwargs): self.hash_single_params = None self.hash_rep_params = None + def _hash_params(self, params: dict | list[dict]) -> int: + """Creates an single_params_dict or the repeating_params_list.""" + if isinstance(params, list): + return hash(tuple([self._hash_params(dictionary) for dictionary in params])) + else: + return hash(tuple([item for _, item in params.items()])) + @abstractmethod def dielectric_function(self, lbda: npt.ArrayLike) -> npt.NDArray: """Calculates the dielectric function in a given wavelength window. @@ -134,9 +133,10 @@ def get_dielectric(self, lbda: Optional[npt.ArrayLike] = None) -> npt.NDArray: from .table_epsilon import TableEpsilon from .table_index import Table + from .pseudo_dielectric import PseudoDielectricFunction if not isinstance(self, (DispersionSum, IndexDispersionSum)): - if isinstance(self, (TableEpsilon, Table)): + if isinstance(self, (TableEpsilon, Table, PseudoDielectricFunction)): if self.last_lbda is lbda: return self.cached_diel else: From 6a25b26844b4f300c6d36de20d5742a660ea5e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20M=C3=BCller?= <49639740+MarJMue@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:45:56 +0200 Subject: [PATCH 5/6] Fix typing --- src/elli/dispersions/base_dispersion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elli/dispersions/base_dispersion.py b/src/elli/dispersions/base_dispersion.py index 40768a20..1a6c434a 100644 --- a/src/elli/dispersions/base_dispersion.py +++ b/src/elli/dispersions/base_dispersion.py @@ -89,7 +89,7 @@ def __init__(self, *args, **kwargs): self.hash_single_params = None self.hash_rep_params = None - def _hash_params(self, params: dict | list[dict]) -> int: + def _hash_params(self, params: Union[dict, List[dict]]) -> int: """Creates an single_params_dict or the repeating_params_list.""" if isinstance(params, list): return hash(tuple([self._hash_params(dictionary) for dictionary in params])) From a17c087164e183c44ecd2ae3dde264e75d861c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20M=C3=BCller?= <49639740+MarJMue@users.noreply.github.com> Date: Fri, 23 Aug 2024 11:50:58 +0200 Subject: [PATCH 6/6] Fitting decorator speedup --- src/elli/fitting/decorator_psi_delta.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/elli/fitting/decorator_psi_delta.py b/src/elli/fitting/decorator_psi_delta.py index 6ce2f57c..5f2bdde1 100644 --- a/src/elli/fitting/decorator_psi_delta.py +++ b/src/elli/fitting/decorator_psi_delta.py @@ -252,8 +252,9 @@ def fit_function( """ result = self.model(lbda, params) - resid_rhor = rhor - result.rho.real - resid_rhoi = rhoi - result.rho.imag + sim_rho = result.rho + resid_rhor = rhor - sim_rho.real + resid_rhoi = rhoi - sim_rho.imag return np.concatenate((resid_rhor, resid_rhoi)) @@ -268,11 +269,10 @@ def fit(self, method="leastsq"): Returns: Result: The fitting result """ - rho = calc_rho(self.exp_data) res = minimize( self.fit_function, self.params, - args=(rho.index.to_numpy(), rho.values.real, rho.values.imag), + args=(self.lbda, self.rhor, self.rhoi), method=method, ) @@ -390,7 +390,13 @@ def __init__( """ super().__init__() self.model = model + self.exp_data = exp_data + tmp_rho = calc_rho(self.exp_data) + self.lbda = tmp_rho.index.to_numpy() + self.rhor = tmp_rho.values.real + self.rhoi = tmp_rho.values.imag + self.params = params self.fitted_params = params.copy() self.angle = angle