From 5ce76bb867072c1d4f53533a114e4f271b7573ea Mon Sep 17 00:00:00 2001 From: titusquah <46580668+titusquah@users.noreply.github.com> Date: Wed, 7 Apr 2021 11:27:10 -0600 Subject: [PATCH] rename for rtd --- docs/_build/doctrees/environment.pickle | Bin 154128 -> 90809 bytes docs/_build/doctrees/guide/about.doctree | Bin 0 -> 10610 bytes docs/_build/doctrees/guide/install.doctree | Bin 5675 -> 5473 bytes docs/_build/doctrees/guide/quickstart.doctree | Bin 7735 -> 7713 bytes docs/_build/doctrees/index.doctree | Bin 6297 -> 6242 bytes docs/_build/doctrees/modules/LLEPE.doctree | Bin 0 -> 200180 bytes docs/_build/doctrees/modules/reeps.doctree | Bin 201626 -> 200180 bytes docs/_build/html/.buildinfo | 2 +- docs/_build/html/_modules/index.html | 6 +- docs/_build/html/_modules/llepe/llepe.html | 124 +- docs/_build/html/_modules/reeps/reeps.html | 1735 ----------------- docs/_build/html/_sources/guide/about.rst.txt | 61 + .../html/_sources/guide/install.rst.txt | 6 +- docs/_build/html/_sources/index.rst.txt | 12 +- .../html/_sources/modules/LLEPE.rst.txt} | 2 +- .../html/_sources/modules/reeps.rst.txt | 2 +- docs/_build/html/_static/alabaster.css | 701 ------- docs/_build/html/_static/classic.css | 266 --- docs/_build/html/_static/custom.css | 1 - docs/_build/html/_static/default.css | 1 - docs/_build/html/_static/sidebar.js | 159 -- docs/_build/html/genindex.html | 103 +- docs/_build/html/guide/about.html | 272 +++ docs/_build/html/guide/install.html | 14 +- docs/_build/html/guide/quickstart.html | 2 +- docs/_build/html/index.html | 20 +- docs/_build/html/modules/LLEPE.html | 1123 +++++++++++ docs/_build/html/modules/reeps.html | 31 +- docs/_build/html/objects.inv | Bin 705 -> 723 bytes docs/_build/html/py-modindex.html | 7 +- docs/_build/html/search.html | 5 +- docs/_build/html/searchindex.js | 2 +- docs/_source/conf.py | 3 + docs/_source/guide/about.rst | 61 + docs/_source/guide/install.rst | 6 +- docs/_source/index.rst | 3 +- docs/_source/modules/LLEPE.rst | 15 + 37 files changed, 1716 insertions(+), 3029 deletions(-) create mode 100644 docs/_build/doctrees/guide/about.doctree create mode 100644 docs/_build/doctrees/modules/LLEPE.doctree delete mode 100644 docs/_build/html/_modules/reeps/reeps.html create mode 100644 docs/_build/html/_sources/guide/about.rst.txt rename docs/{_source/modules/reeps.rst => _build/html/_sources/modules/LLEPE.rst.txt} (74%) delete mode 100644 docs/_build/html/_static/alabaster.css delete mode 100644 docs/_build/html/_static/classic.css delete mode 100644 docs/_build/html/_static/custom.css delete mode 100644 docs/_build/html/_static/default.css delete mode 100644 docs/_build/html/_static/sidebar.js create mode 100644 docs/_build/html/guide/about.html create mode 100644 docs/_build/html/modules/LLEPE.html create mode 100644 docs/_source/guide/about.rst create mode 100644 docs/_source/modules/LLEPE.rst diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle index bfa0d3321c32cd0778959fdcdf12898c76afd848..9aea3280c93fb7a0b7eb137cdb5ac630c3a252ad 100644 GIT binary patch delta 14660 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 diff --git a/docs/_build/doctrees/guide/about.doctree b/docs/_build/doctrees/guide/about.doctree new file mode 100644 index 0000000000000000000000000000000000000000..ef90f747c944ef148bae33840af41ee28aba35a5 GIT binary patch literal 10610 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 EzvBrniU0rr literal 0 HcmV?d00001 diff --git a/docs/_build/doctrees/guide/install.doctree b/docs/_build/doctrees/guide/install.doctree index 46eb899ccc86a154a7585cc54f1bb95ea3a8edf7..88d39c3469c9d7ff2f47d537affc277017ada63d 100644 GIT binary patch delta 288 zcmZ3j^H7Vmfn{o#=tR~ZjG+_%8L|5KxCXdx_GWZsWi+4M%O;_bo>`JnnxvPUpR4ca z=cDWF>gpQos1H`A2b7zV!5%v$L$3#{SQ?Mw%_i)fjN)M-`3lMTc_l@eNu?#J3MKgp zV3Q`_=kUNI=Qz2G)7QukXu3jjPJUjhLPkkRL9vy-J|6o(j+<=9rAnSQxy>)QoVfv8 C#a?>= delta 530 zcmaE;wOWU@fn}7MEn^CYEI8=WRA+ zbY!g$EXXWS$jmD)NzBPn0IJr`C@Co@w$j(HfN0fAPR%LSOU%pBOV2OUFD}i^1sY;x zU}T_=%}hO@rYRZhu~RbidWbSvlW3zizhdiTR7woVS4hs!D=ErMDlJJ>D9Kj{E=f$v zN!4{qEKbeI%u6kv+{NKRoHEDBTAaS=%|ItBB3a z^?jpx*gVlWZazNrC>#!U1Y2;JKdJDs`lS~RG9d8X60an22Pbh!k|q)k%4t5yrBhO2 zAk{Y@@o`Awnt%^CH8wTY;llyod$@QCw@dMqD3UrH4hQ_WT@bKAm9a!*BAp^Jf8jwQ z5KbmBJfw&*Ryhp>CsQ%LmuIov(LGq;dx$6#KaLWD!}|ikNHmChQxaoI1jk64;|1BT zfvrn}Jk@-N^rtndsjGG~^ndNF+Kpp{9qP@WMEmK({3eSr6lyhjk(pMR+A?^?)JpG| zN^UNd8K4eT*u-rSgZ4Eu)GNU)TfVHn!>?I&D zEpZ}BH@v2kE?>0=<>eV-K4ujpa-q-5A-dq&;_))>?}lq&k&Zbx>w*#+>D%IFZLT=S68`J!? zdx-=8un;jU9MvssGAv}vtTa%?B!_YabhI~Xz+yC-K6dYLy;w1hxoNcKbUvDzK3j6j u)I19_i4i6wab8!W+C}}BG|S$@}Xl`)4HYF{H|ys%+NHnv9QX!jC2w>T?*`C zvY`MoOdISXj27Wc5zcm2z_}uf3E_Dt|2!)O3*nNLmSNF=y&7tTpiVp&aeZI~CWCi~ zAHM{5OSgsU7LqWwf#3NNep5U`8Dk2HhiC7R{@+_CD(xhJ&*wK&NlYqL=JbrJWmv!Mn4hItDWowWjh~i6 zl9NQgC%!*(eT^g%TYl%7EB)Zwg4^d)vg0?d?Jn0Ihij?JHP3WU>ldn30%Yqfm6L3q zp_1HfVLTYa3)RcQ_kP;xWchCOQBT!n$7_TchHl`3XtbP;4tv`tV7&LZX!H(=V3h)z fb4Lq#X1aTw=yID~7SuXx9)~rq4kpX-*e3D~k!P8B diff --git a/docs/_build/doctrees/modules/LLEPE.doctree b/docs/_build/doctrees/modules/LLEPE.doctree new file mode 100644 index 0000000000000000000000000000000000000000..9b52f4acc218645f27e81c00d30921ec5c9856bb GIT binary patch literal 200180 zcmeFa37lL%k9v_b zHiBSqY{O+S9MM-oVQ?1Zp`31NNDgyo%6d)=ygt8UM9%Y1=f zVole2wyOHqsZ(|TY~jmJK4ifm_`mq*N^_#ysny$Kjb?6xMt+4vABF;T76E3L3Gd-H6udPuyuQ*P}JW%=r&xG**rl(ufY;+iWq z%~qG(JR29voldK^tJ{Gtu*t(}l{Qq5mrT^l?KULB--{aMDf-vpmF~3sv8z0BODmkD zW#Erm15ib#?WKcD%w`1e5^}8Q!?bkg&}0H1oYOb5i=;p3#ZGq)@*zf72!pk@Zsv>ZJpUNis@X=!9c_X ziasP>(gs1*nhk>~91Dv&wN5=$vf@R=g2*U<{Ds$t`*Ec3R8Oz2+_D%bx>7g2Vs#5ohzr-0TQD{{VDXfIaV<%9UWRr{KArDS zIzIwBKacm7u=^zOv8LC>ZLmZTQ+y&3PEaJXKu~K?4Uf#mM-UqwW3toK%}Cf*UAYYi z*cdN_X=_4}IHjZrIY(nosa=B!qSI}uTDj?2F#VCni(u8IO1V=W4W_GQn2AdJr|PB2 zT0KPLXNhDWv`2&To>IG6-wP&6Qbq$%W~)5WDT{z^>9D0&Pv1BYQtay zBQJp&rDxV_?atoS)$UpOymT}l;(7&RH;qyrkcAVwBNm9X|mfOhP-)r zYjgLSr(So(@Mr*qYg4sb!`9~E_C#%ZrZfd}2qr*YC!CrV(kV@sJ5@$V1*YOg1=)sq zw>;HWyPN2?JI$#Q?N!McAqI(BI)-Se+-NjA<;1v(7gNLF#>;<7!(9x9dv})M#!Eq; zo7&T5T)YmSZo$0X+6xN@45mAw?sKU<$ECSVwbWXz^DMVvX01n3ZefDNMT;-7uw*W# zZ3tG`Po33QXR%tnuX;c7^-9TC^%Z=SEG7LWJ5?vAadS>W>Hd{Zmk-*yJf3t(OaiAM zG-rk3oA$vF{Nvy|D1%RoJvu`Q6)Eb859hj}7P&D#il`H2Q!-;^6dhOU>gM~Yq%Fk@ z8na~tLwx;5`~7<`rjI2{s)e|rKMV!`2G)R&@#N z_?l5pVKbeBBM1j{QAkp5=8eZ%nojAX1_gFXe`%ESqVyPLQ6lpS0lres_WbeKNY|*2 zj)Q5zAjnF1bd;^AAMMK8X{Fto540!Kb=pU41legn%U0Bj_9OWcZzJ52IUehYI>}G8 z(P1Zfv#qEX$wz5GC~4NV-0?V(s#E=R8!2|GZ?F~hqWTo3Y#Uxn^58Bz9%q)(`P*Y7 z+RopUt!S_O3CKzKD^}%bKG75gS2>y&(gE$ftaJCR(IPZ!6&^vq3))}mr zljUx`Q`%ducSF77cv-tUJ>6_|O6{p~tFx!uMAyjOu>M7FRp|*+_k-zc$TDSS=KBCc zp-1b5ICIHloz0J1*sR(W_%JCN-4FkWr8h%cpR=?ay%7?Viz`{7*m~(fCA9NiRC1K& zy)Y)R!+ADWhD^{kF=62(MH8ibn%HA$Ia3p?{d}5uwyl>QG(kJ>MH8oHIgSj)9B-E9 zDzati%DUgeY>KjG^C|1imX#oE`DwN&`aI7>>3&dGah{q>+WN=7egJ2~B>XDDr{ zC@UC~DLP~S%feWSivA&=ioRlLIa5WfeN9EjCcT%OAu~hg>j$>pdr%l9suzW=NO7|o z+B)7W$hov->3kk~n7O2<=(7v zEz7l)lB+B10t>?_3foAEdP9jTEG=g$jJ2OnVb|Gu=|N$%^IjD8xU{1PLtV=(CAqS! zdAh3V7FJVKRn4cWTP-bTs*1IrPgQr>dg(z`v~zD&xeI>KcFM4@*{IJbE&+BBxiLNx z52fIov9uo!wT%LiDic-*9M{|FA^*(@U&7lgB8iXMTMPG1K-9zsHlUs&x-)$#l+ zdIPD*kI?9OtfItQ%f=z_VoIh6eTWy4^S#;X;`s2%ZoN)Dns0~3tIvTGZaBOmAqvmJ zLppNSOHa=xnvIScpu(dH@Bn@(9%k%rm8YwU%Hz?kwLRThWexo&xPl(r!s%;p4L>0d z!ZsXz68{qOY2~XV=78wCRG{B>2oy#5A6PUP#OWgnGWrH~M`a122yo!4o{X0{EZe5kpH1HV4C3 zZMeF)b!3=iE8Z8LUA*dvBO8j%)^1h=-2*&20IOGEc94XUMZ&uDNf{&_3;b6ceZq{C182j;?G z70fGK(_(q_56pwVFz;qcOnFZ?Y@^gvaL5JEI^so>O$}_~>eFceM`x-zh;fDh8RNg)B$02i!uA60+1SWD%L0IlJtaATS!Gx?Am_(X_~A zDg9l9GYDUEj?RKYcSmQ#f5i+P72%g{Rt|(bmCYlIkypaPqMuV> z!1plKfJFcw5*H?GVZBmH1Q@bSJ;Y>_Y-P2iIk;P7HOanHIqvt4o5r>4r4E7My6_Sx z14Unm|ImFczyR6*u4cvJ!Z;*8Vi#I#pb=!d4A0hm`9922DSi?)=91)(?V& zVe)G=_q7AKfe}<{ljJ%p*by7@c8YFUqbh zU^!l|5r!4mPBvkixyiob6mG+9-~qP#kw0(;#D38o68_krmZLv-Irv4&jg5_wCby|Y z=?`8MevysUwlYLb?}qIT?BV1FZZwE+UyNLk9roDMs&LhU(Xs}q*mi(O4NHhgf$yq_ zO4@jPqXy0o2xIOPYGdITY^88RHyUh(0QRsmF?MNKZ|=h#Z<*|f0V$E7YOJWp54O#;_-r%<>8{4Dj_u2!u(1bLO1@K645I{mYCH?2K*3edTfnAFN29xgW3qzsIGa;iI&$7xT5YsM zgi{U66_d$B;=^F)V5F04wTM2=Et0;jE+TN2>!1CS+6}2_CWvSxzvod)>tTSu9wunBb9%334gGKlzZ$C18R}JZdo} z7m}h*6I_V?15POYAr*24rS)`=UUh{att&lh<)d{pkC%h zrSaZ(W8A>yv0#H@jDJd*yvc)Hoyp5U!8dr+VoY93iaL$)Fz`xoI?3Ns9eXIHO^-fJ zO6wlA@=_M*1 zNLcK=;`Ey(jQVGe@kiout%>ZtsnHLm5cX~km|!dYUQI^z`*1@$sP9GKhK%+uON9olk&XEgL+d_Lmss> zp8IZ&=W$sqI8QOh%PEtWdXTF#xdjya1dm#b$%{!*r#Xrf9XU^`Nm4o6Dcw)^punX2 zMvq$g=-!+)TXLgJJw@W0nPV$bIxQ-lX%9Y4=}dXl(xlUO(>AsXQ`4SIu zbtd6)w&;Z(wHTAnCq!V8!r4XNNu3SU{ghrbUWne|*)4~mOLZ-+GwN_P#EZ%h z5(zg*mavy7NKoWzTA3eD@<5vOuCYfVv7zX%JkDnxq(?b#Aw{Ey;2+&-ruji{`Ubp3 zV0pC(&yewacqD)e0_0i&4M7oaRkaHc4jP1NxIwu-@w8^QUI|(u#9~31N(Dj?rf2X3 z5n@gPxO`HZB)_1^b^sRu${mO}firasWm$`f81n+ZFM}8qh_fAT2dgk^RWRACLmUdb z`#?kFNl+2QcR(NtjnRPfa2nVF{f)s9uXg&s34SKt4!r7|Q4Fdd#i)9EXMy~8!)O4Z zCgjg;aLJ;yeho&3i~vvM@j3_ypy3HDAcO^hK(so>fxth|GY=7Idmdme+Lh3_LFS^}BfiqU%%OR&GR#bOhzuXu@Z4hI=S(IJOt!^-IIpfh|( zl}9K1hK%o&=meUn*=4-32kYb0cu#c&gV71V9PoI&gGVteMS$}-)IwgYQ3rh;8l3Z+ z``e3@(JCsVAh(RJ&ydkMIYv0=I9DA>|2j(lSZ?~CmO=lO^E2&m*5<1@$@SwY*B9sJ z`so>5pWDkc$$9LpvgBwh<>)E7Ioh7V(UazB66Z|LTYHl4Kc#%%l$-CHGx*+`s>xng z9hmUvw^1p9DwV)wZV8k$33$P-M?KJKF4z@Um3Jly*1|~Q5gBDlAsK=pj=!#iXhedR zFpiWb29O4qMK7jM1At3Vt8Bhawte97#0C?KzxHS)TlBRwA!V}I-{}ysvDiPCR7hFu zX{Nr|uPQp(=VQsIs31Rj6*ZDqI5;qu@?BsgNU3cpzhEw^0IBp`U-I<;`ys3GYtFVq(X{e zi_8%UU43{F^u#$TfeNuNG|gO_$B#PL#Ziz?kr&8Q1@dIBsYqGCsY6+mzHrQSm`oQk zBRc$+5hpTF*R^G%UeFPH6b3zE+qw0$+`(RcJzb#RYD2F~ zVyR>W;I4}T7RGF|Wa3F*rmjS)kQ33@l@5lC(RFE-zC`BPo=ga0V&58|IqW63=APhS zG{5G?*_>g0?7*T&QCwU(!Ye*_0>QzaO{bZU8Z!-NmJuQ*`kr=(!O(X-OWz`MLsH)! z%nHKdUTT9KVDGu@pym)jemi)J;8lEf1V^w9^ox?h1j2fV*ZtaKPo50ur$|H@y|sO{ zdL7?_+!{+U1`rsGs8;D7OEpQ^-70G4V!v- zmQB%2efw~Z+q{Gz37fE<&4=J?%2o`XT_&^AWW8AiUqmHrv}>Ii z+>Y^WSaCPpHWgD5Tt0=@r}pCQ)o?BR2q(IjurI`CK=2N1w+)9glda|yE@8I$CHdMp zn^%&_LW^WR`Kdz)#(eT)QXvIH(oB6k21SPm_!w{(6r@Ls3dHUYgMab`@EK`L!C z$3@!A(ThUTr-S1NrP0TsYkq*}Hw&EX!6KUlXdghsx=}bE%w- z6;cK(&D000ZQ0bA=Ofi!P>>$I1E>H+zs*6tIZXFKEu?bL!}LRh(jJEC$30kN!<6=M zFor4Z*<-MYVLGGf!K3C-H0vRWT*LIH5r|uXNB7AmGzl2SPj>9{4F0fbGC{{UyV0IpBM=a(x5(Kk6vKelwaSlGklU#)4rrl{_XSlE`P4AZQC- zTrGer0{63H!EFa{BY|I6LQz2f7&b--Cei?}MXfO=B)&PgZPjgYyCbGe1BHUE)Uwlp~>-T}{LUg8)Cyq(@BQ@NqCOjF%lG|1%yZizUTWMv2 zSgXh~`(KW^%UEU~Ar(@VS(>RYvyI|FCojHe54u|+)@b1o1Ogli|K!_Z-wI0)O3(%Y z{_p?PNtx^Of35(eWgSl~>)70ubwIZ)^~K7Y?u9SA{Bh+AhPBm=!i~i0#qddD8Nm6Q zRaTa_KdyWxILcb-k}Kf>Htzpuo>XdoG|$HtFp~#Y1V*iNOv$<5!@XUfv9)gxoav1U z0jBvCmTSy9y2gw-7&09gFV!ga+BpBf0X}m(rf=E`FWj0R9i-<;-3Hk`!^N0@Tq&C5 z39vK3@Re={g8p_zA(Yx1UOIzYS^RD~Z`+6QjTrcp2md+@+-MTT z;nI^?9$!zu^6bh~xJMMXwazcLi#j~&T&ZX6hE#+c*_&wuCUK+|-u@iE`Py*0UY-bp zRhYJlKekK`TKM%c@Lw<#LSrZJ;a~pf@0DQKTXy<_nLmc&Y!lXuQaD(b;jomxrzx6q8C#wywJgfX_K$ksnoP^ftdXB ztvsCkp6d&~h-O~pU@Es}{zB(j)69uGchZG(=T3F$BrFy6bCZHB$~j(4>n7mhI~)w4 zR@m9eL!>Coe@AM)3xOokfe~861AvJ$kH5@d|4P#^d%*}?9`n_l;6M5_2ngu^l!r+5 zShzQ4aJ{hyOv731sjlSbi zkC57HOM%(a4F;V(J$blPAWW_rB}EE+>5;^MmcT#x{Iw5&z!!SdVlPH4AVr<;fgA>4 znA1!CmXrpR(v=>4>ZXpAp5jqUqcnR4VNa|6&cjUE)2hr&-eoAaw>MQ+r0f^C>{9ck zJfBSE`6Lg?=<<9BBHu^Ivj%Rr=ri{4}e=x$}o}<|rk(TXYnGAcw&}`5*{#cZf$Vjk|uU zd^xDxbbWE5q~uCroJECkh6h`^s^1O52s~;rVVp*aI#qp&Rn^pE$9c*uBRRX6a<<8X zMw7D(JZfp20NEQUW3CEIJd&XDF~P)pE4v-eGq*Ll!S#g zJQ0XzRJ-uKK>PzsnlZHSwL}~%EbMMHXqRUS1vPLc`2qS(!_{@eo7S8=c0qXl0r=1$ z{q`cn={72G0@nzt>*%|IaQp~ys@qAy3r5L5=iN+B9UFy8Z7&|DAw{erOSRbw*x!E{E5PE_`!1hFKdIkbzx$?w$9vK$w^s_|$mlzQ{ z6~e{c3Uo1i-PY^IuYm|%HK2q1okB*iI;e+@;{Fn$dc;-{c5rZD3K7knHuO-mmZ7JX zn19(;qSPj;GhEE=vk|G)v$_o+JNQTx>F zi>6r-YAeU4mLxtBEU3o zFlV_e8dIhoHU_j4oj3nsXo;Kp9}2m0czvA?udn7FULS$s^+i%kpD2z^uDo0}W`?BV z5lY1mb5rp#NriU$IpRGIIC(j02)fDK`+pro=RV>5xa3J23)}k$OSlyJ+J}%^R5+SU z>W9NW`J5=9lCshCy&`K)fn_c=U(iH!d7^`$+`9ZnL2owX|2eEqP?WD%_U-gVbHIpP z!E$_Ykh(YJ%NhFWM-b30{>;UwZ>B`Q(SvAx zY$4IF_o($++5zP++j{TDqMB6x+@pi%(GHTBD7!1d%TV-A2Pn_&JB_~uqh-1T&r+2p zYxNK=mv^efgG%lvkYC^^nmUi#-xFpgbc1^*rYg>ix9pwb0TCw|KWU&=y%A$_}B zG*0i~qrUfiHZicojVSr>2_}>mS8-A(JlL{$*DVnK|74|l3 zh5|UX9wi0w(T}L@{lG!LiEsY{Mi0_y3lo3IW6c)0XdZinNliRTHSvFPYvS7(nmE-t z3cv)j3rPx>97C3eMet9)&FuFz3ccpFfA$bVzB8e}hdafUa2gI)@N+}NS>5)3%mg2A z0MJpOZ<tkg7vo-_V!Xq$2JA~J4$Vy0`hf(iSq7$6G4pZJ8S|8U~F6>Y>Z9ea%=AfBa)0;+VS|x`ROL%xwy$g z7|e5Vy+=`fclFZ@xt-g~Qo%LfTV-hpQz+J<=$70Z{UU>->Yx@fE``B$C?$6rmE25j z$^9xra_{=XTbj8J{t!u*+Ps@;^UmDbd^AIwr?_AXOlFy7Bz^Z$`d*fszTax}dF?ia zJUYp>+lZSs#E5}tSr3oVI_xzA@)=nGq7YehF-6?;#Xo$*p|Sh%n*x^l_)&PLhMuG( zF2ygi(7YE4z`EmdH0NX-N5Wn!VOD_w1heMc# zpDw%AH^F0^K+0djKd!C#>uAvzlA_tIzJM0J%cIplr$xVk$GM6us^{_0BkJ$19&=!} zSUnCv%rf}@?_KO|VW!F1enS@EMZ{yT^4$lO${epGa`BBdKv z@ROpT?Trub=RZ!f3I6kC)ml;Re-Cm$i4=|Qhx2>U_kzuQPKCd8m7Zg1IeH^hPBtFh z2Z^%%2h{fI*F+dMwoi-viLICSCVPpOwc)APW~)RK+t z(Fs6mm8_B*<0C6!d!j@`3fjsMc)UmggXjT3PJ9hvSZSOW(6#ueMGETT6fcEW%__|) zxTr82A38k)*a{LlGt=R0^cCoZ4U4`kX$>^2nr($PGd_M@L8hjyN$HyX@X z_={BK3ziSx-fnhV6XDH?wR6RBCLD_15B(TxCtJQwuvE~*B&G5(E)^!V=;N4h1U?S8 zA5W|SVcxAt?SvAXPIuw@&su_v=zl?&P@`6vjh6~gIU6q~%9xmqJ`HUuVm{7A-a_cR zH5+}7ReJ;ctBHFXF)wMD(Qjc(QBLCO=u61TB0?rrfA#IrS0OE0hXeqQc@6|9P;1?? z06O?R+s#0X;@6>=Zjwdae}CXggp}Ib{BWj29_`0-`*Np@mzv!Zqadb5#Kj2<%5bnDzNtWDlIQm_7v2qVOkNaJ-)ybL#tTR1pOt(W0g4^QMollX2Q zKTGG%csyo=w+t(E!A?Yzh?;^JE=AjBMVDsVAx*>c(j*m9JTD@1m$=&0KT!xq;z9HV zqYt@LJrHt&Oiqnr9Kr`s9RkLp%->fdMV-c16Z;?Sx~VQ`+dWge*>PN_nq7vqDSIi` z?&>uT)(zWw1*wo?TOxBuifzpg%hIa)+Yi_ia+KSiUhWWTetWt|*c00t5VNArW6Z*r za%GeO2@Vi7Z1d5vBA2ztXCpI12%F68pLDQh*vsE#*^9^=W?oVRrt>7Ozm^T!hy_hg3{G>;#%n6U!r@Je=Rpx}hmK2SgFzqO| z&s3bwFjpVNVKv=x3Sko)7&+eDTB$oF6axcmXG2i{YvVvnb>)^MSc`WQVf@k{I%)u; zBshyc(9OO(IU=s4S48C2CK4Y9uLv|c5FcDu;^QFRjW}zy$%lDpQI{>E) z`rsh`3~k3BFXo%}3h)&#f%%fSI^xCjxM(&$9NjO}{SlEOjV8u5?kCRf>NCl#!48cA zx~cB@)M_>OR7%QsqVX~F)Kjb>^;E@DaSo?itEdCQ(kM^^2U4(yrmKi@9JS0+ny(_( zJYl(H61m-Bfc&%okE@;qW$9!cxBGouW#vjgz5@F36fW&(l2eWzK1q4>5@Z4!ihco} zFut9R596}6c)VaTA=$d*{=y+S)t?tFfnk{C&m-;Qg#A`Jq+aQ0JmJl=^4AA%-iIt? zWqR{IV5y+4QHo6F+naY%>R?Lu?!DJVuHCyQ#cz1`W}w9!-o1ZC0+@G?S}Jh@-3G~Y z7I%C1R&dOan9%s7keV;dJNf~3HxzvjiP=?79bss%%p-P?<^v*}Z%g5r9x^xbTEgVT zV916VDp|ar0?EF}<}8V9^yLs75bauLCMfP+yLE)m51ysP zlo5RZNg0aXhlKi}o`W+}5V^fu%B_!}=N~;}!9dTakf4CgL0+I|g93VB4wN5;)WZ==CmZX(W=c4w9%TRejD<{2|6p(IO5|Bvbl9za)j{` zU=x5av!*B&*qjPw8L-(i2wl;ev;*98Ft5#pj@A><#>!$Jpv}`QWMx8|8!Z(E1Z`GGXro(FYDCs`E@JJ- zMhf3RHY=dT9LT1H1TbVnO_i+BCqc3=vN??-8-87$@lG$x^LfEhN?3YyCw4s)-GPMp zA(?|PHN5PQLm*%s{s$*g7{PYClvf`S%_}`*!Vt}UNKn9LA1_36o@TNDXi%Nhiv#Cj zll)^P@WQy_z@hH|fRDMy+CuHUHUP4Jh{>LV1TSdwRu54!XoI5kgf_DttukoCErwI= z1#S4S6q@yhHva~c+59$N!u0+?n=yhB%;!2%0nQl)z%eV&0p<)Tv$NT7k?y@zkh3C% z0Y0Ur<_oJPfafO;qu{^+Jvl#uvJCL-8wlVr1u+NMBdL#mi;NK1(+5m&)N*re$ixH% zON9Ypg3~gsF5Ty`#6_|l8Aufv$l$x6k{rn3Y$Sjo1FB83(tV#0o?BtUR566oi@IH==^i0y|`Y<+ZJ9D1T#Bn+ec9tjHANcF-f zlU5DpMk(T{N4O1WdsUOqv4iSP*i}5J9>I2(fuiVKPlu@&f;rYhy9~jgc0CcyZ#;$( zLonQkb0HW$VF+XMMlhR!UmJpXBBu8T!3>k7zgF+!>#><}zNZ?%MX8mFSK{e`tn3;o zxDHn{9?MbJD5WAjo8ob=s@LY&q>%WKH8aHEB53F)hXJc-=z1v2(9pq>Suf{OlI&;_ zIWW(q@`n<49Ae9LDwPZ+o^~{zIFz#T*9QmGJr=SuLEeij6$S+IPLvL)iZX!GJx?!i z(Q3zbQuYS6+XgM>z;+KJ0SwzwGbJEp9FkM9ogR|9jYB)eF*%AVY-*8v{+j~0=r89` zfCyT+DzBoPa_|{szMB>EPxQCQ+fejjllL`c^lrF}o^ zsCo#3`4K`SC}6XVmmdMXpiQv>OmzSa=qIMZ7eIq_Ar6A&^GV&iywGRtT?Ldr!z9W# zwb?5?Ir18c_Ie12ZN*VQo?G!<9<8#iIJW~%wGJ=TnFo!Q;SxT=gucAD;%@-TY`Ekt zn4W@5=rqwYCOI9~czA`XRGCaYJvcE5M`-!f?KdMSV>7&7PNt%703p-x(T;L^Y@*o; z$0qT?q_NB49jq(yFJh+Z_P8Jk=O(Ns2`eR#ob*8Tv#6$M`-)#Tbar1lnMgTZJTg?R(?F&MibzLr7%bQOyM&GGM6YTqG!9 zvz8aC35wGdxhRYTSL|1Gh>s@nwZL6aC@*%G1=U`~gvX(P7l66KLlz9cpe#HA%zBSj z8Gzv`=Tv(E7(SweYP4;{h)1go(Qu35RC^&BJ_dzmy%Eh5fifGSxdPMsgJ{S*A<}9} z^o+)SQe?anqSu=Po>3w?^)}Pt40#MhFU-x{mL+VMfSK(M6Hvj-)1WK^GXuH(pX8}$ zEF%ezYRJQUV3~am!OaQFv>lBnu#ACosfhbr9zn(xu~^6 z87X}O%3KRA=72J9Ljo9-p@vFU>+2!e7s`l_5lE)R>4SEB|_JTY>mLyoyXk@3XHrH}uK}2T8g;XabUQ?nsxTnZ>Cj>hi1bV(fI%c`xMa~k z1Co6q(mDJzia$8QmQwis6Pqvtm@xI{M{?B>b4zp&k~I{)7>V`+PLF{pg$Oj30_`J; zdZUM67>as55)`n>$_qtZE%a(GgaE1Z_elz(uxbR_Vuy~Ok0G#KA|nI<{v z@xp5F@=y%JYN!}bto9cktum~}?T=IKh1K{hAne2&t9=6~v-z98h3WmlYU}M#2F)@x zBFR7mzqD9a$(tp^8^ zQF-+2pppzkOHKh^<_C=}ho-WYow=d0;~cFg(3q9QKA^D;7P2zY*jh`40im(LHQ3UD z*r3#I_&-42V&iCAi^dY$*D~IS;#m7~WD8-){6SFJ_WxCr}A% z;4>W30!H|IF9&x5Au`;KME5;lsO=#WhP|edpny$HUfAmj0jC7^!YR^#UGODVw!4+E zt5MiLRSza>bsBvNL1FXMnlOv`Y7i{zc}!Ovr+A^O=Xi*op(_->7Nw zoR+F}Y7mc&(`5nffK1@$S;@PtVHwJDzZibqlO(|H2^Pf7C#S)7Dki^ZEyxSj;WZY6 zR8hQw@CH5S5&f-3GW(9{F7=QC!*owTf&w<1dSSZ9b5jBor-y@!DE@$a_rBJU#sm-b zF=OI}GS_B=>f^b#1ZHy`Qws+|UhwT24=FSFhEn!~Zx?&C%HSI}CQh{%eB(2SP`WpK zI{=i~;M;RBy+8164dNg19bxhP`BWgcDhcGwx^sYZ$_;+AUCSGLC)%m!BOonXST;d) z_c`oAMRfNjP$auSKAlnB`rT3hJ8DN6d1n#4HwB zr$#Wn!$q+jy+{oh=;cMwVh;53StNj=7phFM`n?2_ebLLw9K8s}_0NcdKOn1!7(~&x zu+yRFn@E!1;oHF*J48xfms0BEncJ^CjjY=vC=4^~!>NX}uf~)yw z-Gx4bPBYkpuaVUzX9{7f1?X~D*x46`4FIB*upYq0WbhDis0sK~y=yj*zJxi&1L+Rz zb;)UTtnkI%3z95^N-iU3kOYI9IYq3 zK2{d{fS{gdAuAI>J;zdEKnP0l`UoRR#ZAw0(P_s`Qtk$Bx)fT>fty}~1Tfr0t&|{w z%OTkpH*Mm$3H>=}(cr-;w`RDXr`FgVfFNp9wOi@4`pOggd}A_~CdBZF-i1UDMgJeN z%uk7(-?9#UXv>tjezz$Pg#wAIM^h5~s0CYu5McmnbG6}T5qd8D}9 z3WM%6pehJ{128sO>oDX6KT?6$pt}{O6%V>+Fyk*UGvUC-3yyu>L+cEVq1HX&*x!1z z%HS9`Zceoq9OJW%FhXxQcF0OQ99xL#{efd=5+4hl(85+K9y`f!4Dl*+0A$LebZkmR z3JfH6ltv<-WD}{vxCs_J83dL!X{uQ4L@3L!*wjFrK2masp?U&ilIEz03! znMmb4ON9X;m1T;rM~FBTs%&r(YKJOP@CH;l4qD6sRmPD3234pI$#S|3l6|2{mj8x* zGa;*p7&6f$b~+SQkR(5-G4PQ>gj1Hn=_A&d@el*U8heqTfQ>;ftZ|u`>7+x4TdU^K znS$dV0GVMRTtNl&{~#RYQ{cR`C9EKxm*&8Xp3k(z7fr9F{!R~}v!xz|?zz;rJz8Z; zJ-01RwbxS5N2{;_@1_1dK$*?+@&Qcm&r(kwOPsEj+ZmBO#$$=J{2UhfljXE5`c8{{ zCQl&p9;wGgO%kR`*7(mlbgQoMpN6t*jUUL%Wt@ke7kEi<^i`zY;%yPJ<0;-2lCO*G zhYpeD^R|!<6mQFS9jzz4Emju$;B8qlWUgwN7-pfR!hkT0@}MCzp;Q2~z(u7Uz(}bZ z0Ok{*zZ?K2Kmr(mp*Bj^=ue`#(g=;OD-X@Uki}~b`Ue8eQhhCGPWXl6%itg*Y?Xs{ zv)(1)Gkm%8EG^=P9*QfR{LE0u$t9EiNuA(4HLXt~Nm1`N|&fdmC?+VR3P z!|W1Rss`8c`H;@(-sOcVWA7@U@24|~@|D~Rf86LHAcj9sK%V&HQjb;{{@`}NsrJGj ze6R_9dE<}g0%bP*aVMtt2Y-;4fu>s_I#uCTJ^qvGZ!lg4((BC=9!bz*cpk{RKgph{ z4coGWxe|Qx8i(#xeDVq?%karSp8QDi)H6ajQs&f#P<=e7 zX252S3g~#^i@X;!DL^Hcp)qKJ!u5nEzw%HSgC^XDIMrUzgijqp*WS?N0^rvMO)kRp z{y-BFC))|9rb{ppmGI77X}a90ros~=PS&hD2Z*A?$(rq24oFBKtXY!Vepu2MwXUHOnk4nf#zaUG~BG638QnyqX5M)t8qb#ycg)KD~#dg>t zHDJJ&tDwaku;nf!fWa23OtR=b1(JPX%NYW;z<|fYdlW#kg&`mO{_GeZNnT~eu!>%T zWDG^GLL&Wu!oeF&L~i#>xoM9C-*bJn))wLj;O}x!=}y;c6A7;f0gqN0Oyd^AsrG_te5w?h z^@eGW0%bOs_FGKv6HKc&cbBHZa--CScz5vLlt{F@A562?odZlekDhGV+b!ywmeByn zZo_BF%66*n<3=rAcB^f&`+Ql2UWhUN9wvsfZAF(uX!r{F|C8YV@2lRAWgmvJ zt4LXe_9e^&l0@t2rw3q`r--ZM*=uDMKAmEXZ>=$L)~`vp>R8hGBtHy8+dp$S|G8Ub6t z0(xDmIav@B8~C*VBGq=0!_Rgwzv!8;tH!GBI>K-!E^aK8N!m5euHdwQUJ8#9a8KQ)|lvs@3 zqqJ+R-Ym2+2?ccSzvAFN`a9_2E|>tLcf%i`oBvHJfF&(NDrfc9MCMB7drl)?REu3D zS(oZIBYhD@I@wo+|JA`57JZPi^e?1nh`l9t+L%Fj&9H0 z<_D?8I47bdY)#{fyZ9|6OEkUNp=q>LHU(`$(YmG}ZC%~WzY3nRrt&M$a)QBY(0tY8 zl+Uph!EZdrhQPYEC9KQK>#)T`#LNy*#JTJs+Yp)8fv;(~ybc^?3XOYv9rgngHm}1o zF}*)thgEPeUF*!0rt8i0fRz>M1sSEn9Q+ISQ~!d}SaG5`1wX;>vA10BhMTuF8{ud$ zQNru|;zzx_E3C6$NOGxD>(qJKc6l#7+TsPk9os3jxIbm{mF0RHn#2Z6q@gw|SPyrH zjmqZhTiwufVH}~*$Oo2|CWZ-_7GCPGS-$0b5C+AIpe%D%+|uvcIT{xU55*mi3DFbb zs{W8=7P43&#Vev0;u<P_a=LZm$kdoaNN@=wWrFh&Yo_w0}tiijSJIT z)y~tWLT;?+@_9Y!Q*rqmp^QyKEsJR;XM+FK+4#DJjjCM@UnWJp)$oX=QNdK_@dZml=9k!g9GH#BW^_%g(UCR=XySAw|74ak{1DOii%%^J!wO zt(P7&K|AkJ6UQW0BtsbmS%B+9P103zm4&60!Et#$mE34)Ia4L9{d_8^*m~(fCA9M% zRdTeB2@H)a75Q8tN{X(J0~UT#^f8l9A1|=9oT(4iem;G?!q!U<`kSJAsHET*XF?fF#nu%+cp6|wd;6)hqFl=H%V=^T94)=v*gqTTnXq+^o^ z+0f4sN(s)7mZo#{JquSUdihp9z5LSBa;9Eb`uSLL7w2Fh`JvN)p{ z?TYJ4>kq);SSj4YN5}wr62mXL6C?u`euqOtxV0p%{2(5%LxU*lcBC@xy5W2uLA}yL z7R>*9AJP@DNnt*ZpkBdEh5F3MAqNRT8z(Pjc4jaPjg?Uh56lq0WtVs|a||!1g2_Ty zBR&XL?@qzp6#D)dQ|`l{w>wmY zvjaQ5dx#Wup5A?5ZkV}k2lxPN6FxyuZj3y6GC)rm9J|t3jgd{kH8}4Qr!c%TU$3J* zgKrnET;uCylda|y9}djTTqs-{g8fZ_{};zf)P;N6VB+;=D;x`;eQM$g84&Qh>Z)g) z6NGzkbWE%ogF-OmtUW8$FzTVP8_KRCn}AiDfO5J>5Mn8y(~pK#*8m$TYLYM<;&%Rq zL-xin`3k9!GE8Wue%yU5ju=(?d?7M31ZmOFp%*ZxpE{_M>qc}h)>yd(BMOiTHl5#b zv~<4$zi>@8dcKkr-!UJiFjq?`FfG1BrR=hKpo~j;UfPn%Is7c5oW<~uD+=kwz!I!r zOgTq@at&xIO7Fi`~;6$uSIcj8KrL? z(CqIvB>Q`h!SX!X0Usg!d{Q5=qj{z#F6Vreq)h*W%Jc#c>F6>&N+0Q(^_-MFVZ!W{ zQ9{!8Bud*AxoJB_qb*yJgsY+Giyqp`w+L>aM=4|hq_0r40*DuDRLZSZc?PbE!g*$Z zA&CIm;J@YlH5x#bKwta_hAk!hr;rj^MkXC1GM15Dq(aIvA~K8Q5e-~CT!ag9V*fgz zB(V= zP;aQ?eOc-dnL|_^=~N3UVrNlM47QtFG4FP;mR~XDgj1iWheP?`dIpDajz9>{ezw-I zVB!LChKvQ*-~mEA*Z}G)!>u|{CLs1oI8mD_*KOJ~jtrPijdUSXqSd+6)cdAtRYQV~)#Ty9-~SE2A}lS7W$M z?`Q6-hOJO>bf70~x(l&PbSG%_QD~y;VmWh+TVnk!XRbVQOW+9xM z3@6~_l}R-htwhA%Br%lFG0TMxCJmc7H_IkO=GF7fpR|m5ZyM|a>CSB*>m8yq>?0eJ zvvA_KdO&g(Mv9(V&oI(iQq+B~;pkS_>9!j1ZvO630Ox$4bu^7K7Rf>zWL}x}5X4Is zf`o^nDHOEWngjLG>mh?*wTRE4-P2Ea0u6AhhFn}P?}x>c-fg1W1#Ryb{Ta4OUjy@M zL6A>t4n?c~fLcSdbk$@5!2CpkZ?LoN?Q3ub2YvAK zBH{A)4gh`-c(Mfmza7*269BxLI4L*6lXb9;CNCSeJMfOx1jLnhs?EyCY^;5`NUb=B zNZ=+rA;f52PR4#h&1f*g{=Jp`t3(`*2K$w`H!MolOcv35tM*HckmCWqcZm_QgmKA7 zruZ#vnheR0J4}bLoKA6Ll^>@5v>GFH0_? z&mqH!sN(H?)FMUtN-*kT+8NcMj1Jb4$7B9e=lKUVp6$Vp-?0@`_RXKI4ApHCCF+j{9i6SVVQHK8tkrY2af&;=dat?*NKx-~Vwt7oOii%%^J(IATQ5Cm zf_9!q6YjDYF=<1PQ6D~&aH&lWSrL;@uv8dO#3cJDr3TznUs}1?MW;PvQp(*3nfwr- z#~dM(+mQeoGCAf6-Eg?35`EBu3N#FYs>qWE& z>97NIoKbJf!O>D3$sAN<=JPy+!JKE$LAnCAeVxz9%p%>7k~1zmw1<;m_6ZPi zT)#j4s9xR7rtMl^>)_IGGrx*dNI7~CnJW|DyGW8NOb;FbaWDq2gQ9pr?SAev7C~{p zgE7Sk{4!G18Ae#7Tk0%j%%m&BXM6q+4w?;Re1cR+QHID&6)_{%Lm8a0p$J>et&ER3 z80$e9)OV1jj9b%{;p2z+xr1gy89&KVhR94+1|!!)8Jw}92wTmqj7J=daaswGJGPQr?w2?i=|S$y zIbxGLpGv`to;Vj?!d)s{?)tS#lSdhE$D(Agu(*9b3sQ_gM!c`Q_gCh{aDh zNH%2u(Ja}E%&b^Mp?p`wBDR)W2On`TWauE>6@o=sjKWJjmUedVje8c?@%0O6Y{0Wf zQFn}jc;bwYBmM-M5G9$ve(S+H+gSY?MeEvFsj=SB0iI4CgHItJ2Y)Ps|5qRaii+(I zj#!K>>U->$j_6K>^%PA%x-`6tDsKwFL#7kLmph3JAobS;>e1 z5el$eeG^O+pFA|= zuXGV<_k2sC8=mjWpv4@X@4rR@*kb`yf5abs1tjCl0daUvfAkYXbUVB1Pj5n_Q<^{J zNn-WPCj)4CKIP519WhZxpGESAqR$|6elL>`bbcfv{s$>y6+y)7E~_`&Gyr)0DESNz zJd=uUeGC(auk?g<@ZU38)|w6Jug_mm3hZlCU|;bTSoCGAK_3cNI5FSMcEdLHeQbu@ z)FeS90dGI?ppb#LN07pR%}QxraZU8^(C_feUED#xU=Mdg=sW&!>_grtkdJ62Ht{_z zk_*qt9w^54<`7aL#kD0eUqf9fk5%aj%ZeZ4gu+yYGR?io`>n@dVS5Z5ES`IeA9%FN z_85F-$hF7dYmXSa-g}ITK}fbe#wD2EpFPHCd!jZyQ<|zZYE!ja!{kOI8G3xOy5G>N z&SBH>1d2EG_KM<@9~fl3y9?iL8|7bg3tOdI_LW<^>C?e{Kax9`_(n1dq(6eqaUup2 zUn>kaZuEOEq6=b{hV;ta*3rp)8c>`}7SjV+ctTHJao;6*jVj1?Vna0Itr-^aBR{UR zR8*y?Np6mkIpeXCqO)?FjTQTrXrHa9;?Uq4#$7|3lvOr$;I}7cmn2nBCqb38oKEJw zw!ZCTzSvgOm(1ntGl%-1V4{}-J~mm5vNax?89E;i*@(0A@fK3ldk6a0mX*90zI)%H>tR#IvALKaNMsh#0G}hA<;0W`tpEvtRWCzn&5~9gl=kAAz=u zT6RMlF6EImc8ud9E(dF#hGqdiu$GDS*_vgwq7@W&G2X;Nc?Zru4cDUX+x3i z#r5GD@DQvI*N@!1x*j$lSc@#9BbHk5$QK;Hm+{d7s9LlYLFQu?OY<40FQQuEOw zAoEvHEZ6syXWGGfwlUQo6!hFZyvo6Wp_uzfg%rhz%n|(Rp?I_^lXj`39F;&}*jH|a z%{ti4udvMm&aefFXs%M5fGA-6FDw@2)(n1Ho~;}N^=J!zVpy3QB{sUu3?Vx*DSpDi zoT1B)X6aI7j<7EUdT=A?fzQ$Ti(n|izH;mGBMx>AU8e7ouqcbosCcx>oPjtmdpZNJ zAw}Ize&5&(4mQZ+DIs#0pZv znHa5IKdqXd#-v@%PkS(Z9KA+2xpem&8V8R?fl99329HL`YaH3u$vnjzqU7=v^PQS7 z18-09g}|@PQ+zR|_s3Jbx*c}#?eliCzBhy~x;5Z4u}OEah)WsL{KRI(IXH_~GuMFG zXfZ(@_X|tU=`srs9LHPpgng5F{91>Vac?5}P`5!@I&;SDzWa+!Sum&ZhdA<#k4XrJ zvMoKc_I&9+wC9hT?dl?{*+gb{9)`A}DNfL`XIb>GNlipMQ7P!n?uUQvZVOGB5gvbT zsW2e_+DWN{DBa2STo<`^Cz}+%;beO?8q?iQGJ{VJx$B8h5WOGC8;ahG#QXWf4$^!eLgZf83*02E@1l=K^AU3K1se4a8QA*@4(8nBE^CHj)4_t!gtBhpo)OVNh=lFsx`@ zL68<@;K5H5gdK0v&!<^imM~ue!PbLZv!+oMf~|qF41%==48e$O6UXL9>w-xFFl)BN zU)JpLIGLnfYH?C9%qThSEsb%|$LL|;+~(GyS~{X$<=3r(5mE3{M?5c+yt+So}4 zU%On?+QFBUz5%|9&|(ho^&BLCfiJ2^f)`dpvM>1BOu-i%UL{uEO4w+_=g))U?zLON z&uzhCWIWL@mpQf~21xXBBpu$OM8^Dp*}<7bh@9_{a_&1Wd%!~?441tb$qLxW_QGZ7 z>a!p{-yohWIOL>eU}7^eS0?aSa^_=jGnp*(O_q<3I9M~#*au046f`C>R~4x<=jlbUSU{Jwds5?*m^#+et8UEsinhSsNSwxtUH~#t+P-b(4{TkEzgTIQ1 zUog_T9_}Yzuy!i^IxPvmv}$ueUuVk|L~D{<*AjG@*y}ihb`HHPIl^Me&^dN(_B^Q~ zuOp!>Ltc9ZfxI*s7_P6Q64|(pNXM5gSHdn^?s%L?)de+d6OaafQ?=+V0D3}MuMy8%TVa3k`fPrE!xe?aO9gHdn3zg`Jux|Hgl_4x{ zvAGZypEHC_c_XZU1Ila&>r0s4AA~hVPzrnmv^iCx=h*EMMlGb`tTPOpWmcX8+S;Hj z9cIJD2{X+^UMo^~;`l2yUsyI7WSD=ILTwyw0*T*)iIowxTH*?RTEqL5wD~6a5wy)h~>8wuSyojCO{l!hkT^X_*5p z9jOH_lI=)Ms=z>M-vyQAKx$7w0vJ-G+9crO`)maI98I2jT?k)P!-%}pAu=9k7ZNre zT@Untja}=I3$9h{LDGXbLl9A2jTF;U+`i+m-5z3LIIMzX1#E~&=>T`gE=gXDK+H}#HYQn;kQhDRR zFr$PR3}gU*)?qU$H2gG_WzcYD5YW&n4Feo5PbsE5?6OKpfS6TkJWk2g1^OMEKN=%vHN-#a89+hL+qf&mjh z0WIc$i7QcR3?@>2672IyRIO(O+e!fwGX~3e>?$TKJ=zE~fu*f?C{%%p2X)3E0vkh` z2Lu+c^biaKi02A4Bng$&erFjz+ z9-A!Bv7nhD44Vwcw>h-0Lcj;0EQ5g41A%}F%Lej0QWXg0R-VvWG9}KlWRAyrqAsJ4 z+hk-%br0K$rl7jN@hpp|PKrMI5Ne@csP5Yqnle${H!T$ggz8RIP@QTisUKbXx{Fpj zn3J+Mz}y?5#T;Pnw@3g3b5xB4o4gs4eZkxaJ7iU@U&gS&X(&1t z{_#AIJs2|rk={{A>VS~lSswCW$Zi#q6|m9jh3wX=+9QBYi@j&L>Pf5(qUjSELuPuS zP%~KpHaU1Q0NMGZLJA-gnWyVF2W->}I%1E)V2#*zZqT&R!JYz|P-UK=X~?5h2Aa5i z<^oN8N)Q(04Vqd&nGH1U#q|Dwrn6P>f${t!K~X_R@-yV8D5>PcmRmdt)?K`# z@m$dLH}Hid7}Z>A)dT!qA{Z@rcZ${9c|yF(TnLZ~rEwE59h0D$&z45IFj6vDVu#*U zxO5kkr7XnFLBb^>Oaus3d6)ADMHor2Wade%Waf>>TAD7NH`v5uM@p}?6-_}(uktL5 zNJ&aPdL=5LUr6ciEHq^zrH3sQ285KBTL(zG*XzSBQtd!VirxT9&xICq0Hv=Y0SqWn zEs~}F4oGH8z4MLk4gBf=UwL?}c;d={F)+sCUaE?55d9R1gb#%w3x1(&2Vr&~;(SDk zv+oC}3pcvO!I0AeC<^Ny(>n+;dcE-=Ql#D798r`=fRh?Mrj9SkW53032XkbdW3 z{|q5{pDtA+ zvYhr=-{hZir)=V(f~1f`AvH-@AQ=L;I4nj*LA#+WLqX9XP!N||0stM(Sx=&$M23Vu ziHz}BCg{?*-6jn?=y{f{XbR}L)w3*u9x3E#2Ibu^(DRoTnleGpYb_N91bU85T7Np; zd9{m5JKm8}H}K9bXfX%g`4AGo@D5cVS)ePB?2C7%INrf{FfjJ8RhxoGci@S+%VEG@ z*}^9bMEpJuhKcsx624qO?)Kx?f(zwFC1{b~ZHWA?ccHW(co76^H%aF|I7s>@ISEx z`Mbg;-cwyEw{K9nea%~L(O0p?$}RY=YZ^b7T^B+OV!hmM2bJaoc&+eDIB=A>Drsk{ z$cuTC-4+;W3$3(XZjcZ*`AwI3X-o3^W6JN3a`TI?h${T{9U%YSgF*(7e}g;)Y^L{e zEj|quLB0si-ZTlCtzfz`CZ>jw0!{^FY_FXzcdD2#GKtcl1v#aVreFdgMwz%}E0;-z zrDcLp3qcVloSQv%4Iq6isgMGsMdlWCJkA$?g5r2yn+4lKdUHedqZ}erpgJ0fCscpb z!}J(b=OZE)ROic>uvu@Yz7_bjLG^1ey+2T$JX#m-ho_?_I?h92S1p* zY6vLK@T4j)MGW*8kR$I}OjmKWkvbgb(nuxlnlzweI{=!02=Emby zmI?#fcqlLBCH0qjG=0B|T>B10ir?6Q+zc(|*nzws31B-As!4)icS3UN4n%jt)!E5( zf@gKpp-rlVO(X^jP@t9!Z}n@(o-*4w`2-{|*iOapi#~&LfnZydn%DLtDq)S4M5vz> zLd|}umU+6k4q*8dV@!9E#fMJM%$8O2j;hDQ3i(aw$hLfb2h&rQPev#nADzcf*kFRj z2`%j#&+!u2Xtru$dlt{-_Q&M8F{!sw){&s`+?ZZ3bpfUJo^>jyJU6DdsV-$enz@pZ zH^uZid?B-C39BIM*nfj;vrIx=$Nmp9glZ5EPl;vh=w!aWidnPeB4*8o=f)C^ z>e4#?Jd3pKOW4u2qA5#Q!LuwbVNwavQmEFqC2WI*rpzU5t)+rG#{2j=U*);6g!)of zuu&Ja_7zM@-&nzZ2BRa#3bq9aU@I6^B$;e~!N!=+g~|)L4^1*H-e*c28_E(mz47Ux z_XfnUh;{-WV3yBt$P>4E>>}pDnLCJdw*JW70<{>1=@n+3Pmk;fdJZe4 z_H;M_7_0;rf*o&+onxLfrZzGM0CuT*0F&AQM`Jb)OlUUb;F(Xv&T?V^kU{umhh3?l z?F&$rIUk-qm;=pJ*%?9cr?4Sz6DAdnjFESjmDtW*c0A53ql^2eHgVfg-;ZrYQ&8Uz zJj){LlUj_vj|%OVv_%EQE91w6d z62KrJ)hYogpGO5#kYWoPyW{BO=hmX)Hmi?;d2h?1K=LJo*k9M#L$s&}4*OF%jf(k3%8`{C^>-kOKcj=7!`Wcpl6OE024r4MvN-=iVqh-yr~H zqktyixl!2b(JI?0$dQm-eyYpB>8W51B!&58`H~_m)O(}w4?vl1qwv2ly+0d;^>)yV z!yz>q@oe`V0qf6UdvIYE+90$En-yy)8NqZ^R^>OkPEO~KFOgYAgx!+?`7MX#s9S+= zKv}jGxb@&{1*i@Ze%ur7f}^sK8-UDW68mQs8;{enb*cW^CRID)|COz%G77l@9)=m` zXP#vd@k?bzKSjOu3-OYT6T|u2u?EHYJ3MMJoKHFP!ud)ES9)~7aQ0eGNq+oiHxyFg*q}F({~S3QWG6X;K-e%o&(;unQ$uYQ|D9n=#nMWL2 z=AaM1lly%r%TRZH5U88;Yk1oa=K@F~>qLgc(uoYXQzV+u<#E`BCWx%s@$&CIMTdC# zzdg$$UY25xevK0E7hXQY!fK|6Jg`(45MDms90lp_@Y7s$+7YspdvX?wz6E;7fsijo z0vJN3DkKQ`JE+Zcgsk0E*>olU6nv*z?!fv|>(t71_;h?bz&Qw>7ltrt;x^-0GY}Td z*37vyAqGV>4!nR-UhR-6t}!esV$gE~k?mF_lgd_;+qRzJ_}+zXIZ;D0(ZsWo*9@1T>4=K zk}wX=(>$~!d2LW$>$!Qwn@nlEo_`s;_Xdw^bGL9n?!`6tf&L(|Qk$F%;g(!Sxn$So z4E9=Ga`jBg)vdX?!uv{TT#f10SFX34#QxercN(tpgr(_Lh+oTqx(Jt$-wl~*870b2{bHY#I$DpaG1Ff9Pk)TN)4o~rHdbX#Fi+{#c? zq@6ZMe86T?nINQ10Mt7jj2L^4w~-1ddk&E)K0KH~wje})rZA&4C>UGMy}NkO!Jx9c zKuvk>E?(}@D%)M~!IdkZh_AK6aJ+XHKLE;XyNe%VdVh8oYY?^5ujkbEh9$mQrJfP2 zO71hvx^viZtdm&FY`178nY)k^?fmfREiGHvGZ|Qi{fPyMTinsOGxznd3dn;$B87COQtP^=&V+ z$wE`+UgiQzg#qnllpVdrMpFI$=ej7i?`ot5j9tyIVbtW<)m(=Juw4yRCPCi6VPnrH z&ii~8Y$N!V$Xsk=M4p%j>38p7#q71Z7#2|#cmT7UbjVWK%^bY>gh+P+2`2lR>LKwF zyXwt}TS|Mn%}zLb@2q}HNDsZaIPGNsQ@r$TfLa4?e|71el26ogYeY)@nN;exdP_a( zU=6xE+UHTLOFQ7gf<>P;Fc6>ntOtP%%HD%?25c(yg0i8hFA8d5M>`T+y(hV$>RNh zLna0)eiNyXf{I1v)kWlU9{Cnj!(;RF+%!xxNOx|${g)2WDR>*r$rEqC$fH$;x8*2G zt{55Ke7*_@>-NUm-vr8Rc>6n;-XFY8y!rfd3#lmE@aFRxbHLW>+Rr}sP~^>;IGQ-_ zc{ZPv^+aL7WZ3<;L(8f+|NlT)hM5QAv6FlxqGk@4IXXU0WJpw;$N+D?XhN6A@tZ7r z2s;8k+Ez3L0T(>WA_A6Tj+R2Tz9HZZ7Md~<@LEfS0U=<;n=cfXihD<0blP#RlzVa( zi+%={p9A-9K>`@=r79%I>=$hO_~70m!@c-K7&`jEabIrD$Q$}x3Igf+R}cmt?F2f( z;GW@-mx65vJ?@Fzwj-GqPyJO~2-N*M30JCwBUt%p4B(qxA#4N4g|sf?$*WgOM921T-IoL`WN)!7n35&BdU+?C4LCxx-}Twn5afbw-)ZoU?S z#VLIC-EaJ24+R$!QY*_u@F}*)n{Y>IZp6=A9 z;PAVZ3f50DT;jaS9MJko#S_k(E5aD0w3vAPD2-OW4kc2Bm6M_SD~GOC#QrlV%Mkn2 zK)l?XQ^UEvR0x4GDP!A{ITExdbH-yOMHk9Z7kR_>rM99euzitdS%mFU*3m+!);HL` z#zIpjY+r4uFd*2z%rYF(qi}{@gxYbt6ug1ke+>4Y1Gis^1TfrAbx82%PuM8(!R>3f zznYH(&CgbAOvBf|#ZVAs7xc4?-cq1jbk>!qM(v()7oR>4a!hEh3 z*5{2hKL?cAkmeUKy+25kd@EwQT5e}VcpKk}pylU)F*nFXmGw>kDZyXR&(dGM7mwY*O9DgGp{uoX?g z)s*D)35Q!032qk_75y6j>{oF6MHc!qarK3k3IoE`$_F#p07!L)pXZ{|4yxriPEOv@ zw?GRyK=t)V01s^^3M7c|J4~%Up!({>iz}DGX?55t2W^PWq#^CQX28!0*t!Fe&4os| zFW@)F$YU?$2%89szWqW6#dtjL?TWz@)qralo4Xy-SAh9or%R%P3KD5?tgm|_h8?%M z@WclCC)9Xy_)V@2@VHoTQ?3uTa)IWrC?&R^N^GyU#G)?NSh-~($XVf}?+)eXdr-)5 z^qoi{1R_(j2XCJ3`)F@g-YkLj7FOe8^pKIJ2kShA4ts5VBfy(H$SjP1Uk$zwAsx&G zjEWEUbZhn6u2!u(g^}eGM0W|yZC%V5`nKi?H4?z^CWnL!9R7MzAq9tv%#HN9>3rM? z%Hp1?gKc5|xzYDuI7EO0|3M0=Uqy;KRlZ(XrHQJ^3mfb)D|qXQS_DjkV(znPR$J?t zMzu5{FQUnRawr;P7$77{^xnL%JyD-KFsCi=S#AfvAp+Q%QoBhUMn!YKYK3{0pAIyro|js z!mr_^y8Dd?c(d*t_GC%~yxDHiOfq+5B*fh;$XC|1Y%xm6a9soP_I=TO1j@3V+CU;! zZM-FRZO7Zn=n+1tMdU6mwFpGOTRPAsc)3l2_V}MmY(-NxJQsVG#SM>COmq?Is9zhN ziiM`k4NuupVL%%mB?``>!qm;rGh7tgH$PGX#^&ctXfem;=KvDGHa}FE1bCkf$qabg zp1IWSqh3rmKk$7&nCzM@i~+N^ zyxVrS-RbW3A1%;s-M1^ywPm+d5DWxSD*eNzD>R5PQm1cc-prfVnR(-TZ+5#VXe1=9 zxgy9I1fz&zv>^~d3>rR&5fbeO

`<4ixH*UNPCmS=S; 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 BcM<>q literal 0 HcmV?d00001 diff --git a/docs/_build/doctrees/modules/reeps.doctree b/docs/_build/doctrees/modules/reeps.doctree index e316e1aa4f877e05999fba41c7197e7da57e8dce..87fd4d163347f4d7a29f0733b30bcde9983eb6f4 100644 GIT binary patch delta 13628 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 delta 15164 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 diff --git a/docs/_build/html/.buildinfo b/docs/_build/html/.buildinfo index d0738d2..51dc31b 100644 --- a/docs/_build/html/.buildinfo +++ b/docs/_build/html/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 09c8b5742a1f96cda53a1848d8ac9ec1 +config: fdde7da710e99305b2f90b2ffb8f91ce tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_build/html/_modules/index.html b/docs/_build/html/_modules/index.html index ca853b9..d96106f 100644 --- a/docs/_build/html/_modules/index.html +++ b/docs/_build/html/_modules/index.html @@ -85,10 +85,11 @@

Searchers

@@ -151,7 +152,6 @@

All modules for which code is available

@@ -164,7 +164,7 @@

- © Copyright 2020, Titus Quah, Nwike Iloeje + © Copyright 2020, UChicago Argonne, LLC.

diff --git a/docs/_build/html/_modules/llepe/llepe.html b/docs/_build/html/_modules/llepe/llepe.html index bf78f9b..13e2620 100644 --- a/docs/_build/html/_modules/llepe/llepe.html +++ b/docs/_build/html/_modules/llepe/llepe.html @@ -85,10 +85,11 @@

Searchers

@@ -169,7 +170,7 @@ from .utils import set_size -
[docs]class LLEPE: +
[docs]class LLEPE: r""" Liquid-Liquid Extraction Parameter estimator @@ -180,7 +181,7 @@ must be the same order as they appear in the xml, complex_names and extracted_species_ion_names. - For example, say in exp_csv_filename's csv, ES_1 is Nd ES_2 is Pr, + For example, say in exp_data, ES_1 is Nd ES_2 is Pr, and .. code-block:: python @@ -191,7 +192,7 @@ Then: - The csvs column ordering must be: + The exp_data column ordering must be (names do not matter): [h_i, h_eq, z_i, z_eq, Nd_aq_i, Nd_aq_eq, Nd_d_eq, Pr_aq_i, Pr_aq_eq, Pr_d_eq] @@ -526,7 +527,8 @@ self._predicted_dict = None self.update_predicted_dict() -
[docs] @staticmethod + # TODO: move scipy_minimize to optimizers +
[docs] @staticmethod def scipy_minimize(objective, x_guess, optimizer_kwargs=None): """ The default optimizer for LLEPE @@ -560,7 +562,8 @@ est_parameters = res.x return est_parameters, res.fun
-
[docs] def log_mean_squared_error(self, predicted_dict, meas_df): + # TODO: move log_mean_squared_error to objectives +
[docs] def log_mean_squared_error(self, predicted_dict, meas_df): """Default objective function for LLEPE Returns the log mean squared error of @@ -584,14 +587,14 @@ obj = np.sum(log_diff) return obj
-
[docs] def get_exp_df(self) -> pd.DataFrame: +
[docs] def get_exp_df(self) -> pd.DataFrame: """Returns the experimental DataFrame :return: (pd.DataFrame) Experimental data """ return self._exp_df
-
[docs] def set_exp_df(self, exp_data): +
[docs] def set_exp_df(self, exp_data): """Changes the experimental DataFrame to input exp_csv_filename data and renames columns to internal LLEPE names @@ -632,7 +635,7 @@ self.update_predicted_dict() return None
-
[docs] def get_phases(self) -> list: +
[docs] def get_phases(self) -> list: """ Returns the list of Cantera solutions @@ -640,7 +643,7 @@ """ return self._phases
-
[docs] def set_phases(self, phases_xml_filename, phase_names): +
[docs] def set_phases(self, phases_xml_filename, phase_names): """Change list of Cantera solutions by inputting new xml file name and phase names @@ -673,7 +676,7 @@ self.update_predicted_dict() return None
-
[docs] def get_opt_dict(self) -> dict: +
[docs] def get_opt_dict(self) -> dict: """ Returns the dictionary containing optimization information @@ -682,7 +685,7 @@ """ return self._opt_dict
-
[docs] def set_opt_dict(self, opt_dict): +
[docs] def set_opt_dict(self, opt_dict): """ Change the dictionary to input opt_dict. @@ -698,14 +701,14 @@ self._opt_dict = opt_dict return None
-
[docs] def get_aq_solvent_name(self) -> str: +
[docs] def get_aq_solvent_name(self) -> str: """Returns aq_solvent_name :return: aq_solvent_name: (str) name of aqueous solvent in xml file """ return self._aq_solvent_name
-
[docs] def set_aq_solvent_name(self, aq_solvent_name): +
[docs] def set_aq_solvent_name(self, aq_solvent_name): """ Change aq_solvent_name to input aq_solvent_name :param aq_solvent_name: (str) name of aqueous solvent in xml file @@ -713,14 +716,14 @@ self._aq_solvent_name = aq_solvent_name return None
-
[docs] def get_extractant_name(self) -> str: +
[docs] def get_extractant_name(self) -> str: """Returns extractant name :return: extractant_name: (str) name of extractant in xml file """ return self._extractant_name
-
[docs] def set_extractant_name(self, extractant_name): +
[docs] def set_extractant_name(self, extractant_name): """ Change extractant_name to input extractant_name :param extractant_name: (str) name of extractant in xml file @@ -728,13 +731,13 @@ self._extractant_name = extractant_name return None
-
[docs] def get_diluant_name(self) -> str: +
[docs] def get_diluant_name(self) -> str: """ Returns diluant name :return: diluant_name: (str) name of diluant in xml file """ return self._diluant_name
-
[docs] def set_diluant_name(self, diluant_name): +
[docs] def set_diluant_name(self, diluant_name): """ Change diluant_name to input diluant_name @@ -743,14 +746,14 @@ self._diluant_name = diluant_name return None
-
[docs] def get_complex_names(self) -> list: +
[docs] def get_complex_names(self) -> list: """Returns list of complex names :return: complex_names: (list) names of complexes in xml file. """ return self._complex_names
-
[docs] def set_complex_names(self, complex_names): +
[docs] def set_complex_names(self, complex_names): """Change complex names list to input complex_names :param complex_names: (list) names of complexes in xml file. @@ -758,7 +761,7 @@ self._complex_names = complex_names return None
-
[docs] def get_extracted_species_ion_names(self) -> list: +
[docs] def get_extracted_species_ion_names(self) -> list: """Returns list of extracted species ion names :return: extracted_species_ion_names: (list) names of @@ -766,7 +769,7 @@ """ return self._extracted_species_ion_names
-
[docs] def set_extracted_species_ion_names(self, extracted_species_ion_names): +
[docs] def set_extracted_species_ion_names(self, extracted_species_ion_names): """Change list of extracted species ion names to input extracted_species_ion_names @@ -776,7 +779,7 @@ self._extracted_species_ion_names = extracted_species_ion_names return None
-
[docs] def get_extracted_species_list(self) -> list: +
[docs] def get_extracted_species_list(self) -> list: """Returns list of extracted species names :return: extracted_species_list: (list) names of extracted species in @@ -784,7 +787,7 @@ """ return self._extracted_species_list
-
[docs] def set_extracted_species_list(self, extracted_species_list): +
[docs] def set_extracted_species_list(self, extracted_species_list): """Change list of extracted species ion names to input extracted_species_ion_names @@ -794,14 +797,14 @@ self._extracted_species_list = extracted_species_list return None
-
[docs] def get_aq_solvent_rho(self) -> str: +
[docs] def get_aq_solvent_rho(self) -> str: """Returns aqueous solvent density (g/L) :return: aq_solvent_rho: (float) density of aqueous solvent """ return self._aq_solvent_rho
-
[docs] def set_aq_solvent_rho(self, aq_solvent_rho): +
[docs] def set_aq_solvent_rho(self, aq_solvent_rho): """Changes aqueous solvent density (g/L) to input aq_solvent_rho :param aq_solvent_rho: (float) density of aqueous solvent @@ -809,14 +812,14 @@ self._aq_solvent_rho = aq_solvent_rho return None
-
[docs] def get_extractant_rho(self) -> str: +
[docs] def get_extractant_rho(self) -> str: """Returns extractant density (g/L) :return: extractant_rho: (float) density of extractant """ return self._extractant_rho
-
[docs] def set_extractant_rho(self, extractant_rho): +
[docs] def set_extractant_rho(self, extractant_rho): """Changes extractant density (g/L) to input extractant_rho :param extractant_rho: (float) density of extractant @@ -824,14 +827,14 @@ self._extractant_rho = extractant_rho return None
-
[docs] def get_diluant_rho(self) -> str: +
[docs] def get_diluant_rho(self) -> str: """Returns diluant density (g/L) :return: diluant_rho: (float) density of diluant """ return self._diluant_rho
-
[docs] def set_diluant_rho(self, diluant_rho): +
[docs] def set_diluant_rho(self, diluant_rho): """Changes diluant density (g/L) to input diluant_rho :param diluant_rho: (float) density of diluant @@ -839,7 +842,12 @@ self._diluant_rho = diluant_rho return None
-
[docs] def set_in_moles(self, feed_vol): + # TODO: Change input DataFrame structure to contain information about + # other species like NaCl + # TODO: Change DataFrame structure to contain info about O/A ratio + # TODO: Generalize code to more than just org and aq phase (3+ phases) + # TODO: Handle multiple electrolytes ie. NO3- with Cl- +
[docs] def set_in_moles(self, feed_vol): """Function that initializes mole fractions to input feed_vol This function is called at initialization @@ -922,7 +930,7 @@ for extracted_species in extracted_species_list]) extracted_species_charge_sum = np.sum( extracted_species_charges * extracted_species_moles) - chlorine_moles = extracted_species_charge_sum + h_plus_moles + anion_moles = extracted_species_charge_sum + h_plus_moles extractant_moles = feed_vol * row['z_i'] extractant_vol = extractant_moles * extractant_mw / extractant_rho diluant_vol = feed_vol - extractant_vol @@ -932,7 +940,7 @@ species_moles_aq = [aq_phase_solvent_moles, h_plus_moles, hydroxide_ions, - chlorine_moles] + anion_moles] species_moles_aq.extend(list(extracted_species_moles)) species_moles_org = [extractant_moles, diluant_moles] species_moles_org.extend(list(complex_moles)) @@ -946,7 +954,7 @@ self.update_predicted_dict() return None
-
[docs] def get_in_moles(self) -> pd.DataFrame: +
[docs] def get_in_moles(self) -> pd.DataFrame: """Returns the in_moles DataFrame which contains the initial mole fractions of each species for each experiment @@ -954,7 +962,7 @@ """ return self._in_moles
-
[docs] def set_objective_function(self, objective_function): +
[docs] def set_objective_function(self, objective_function): """Change objective function to input objective_function. See class docstring on "objective_function" for instructions @@ -979,7 +987,7 @@ self._objective_function = objective_function return None
-
[docs] def get_objective_function(self): +
[docs] def get_objective_function(self): """Returns objective function :return: objective_function: (func) Objective function to quantify @@ -987,7 +995,7 @@ """ return self._objective_function
-
[docs] def set_optimizer(self, optimizer): +
[docs] def set_optimizer(self, optimizer): """Change optimizer function to input optimizer. See class docstring on "optimizer" for instructions @@ -1012,7 +1020,7 @@ self._optimizer = optimizer return None
-
[docs] def get_optimizer(self): +
[docs] def get_optimizer(self): """Returns objective function :return: optimizer: (func) Optimizer function to minimize objective @@ -1020,7 +1028,7 @@ """ return self._optimizer
-
[docs] def get_temp_xml_file_path(self): +
[docs] def get_temp_xml_file_path(self): """Returns path to temporary xml file. This xml file is a duplicate of the phases_xml_file name and is @@ -1031,7 +1039,7 @@ """ return self._temp_xml_file_path
-
[docs] def set_temp_xml_file_path(self, temp_xml_file_path): +
[docs] def set_temp_xml_file_path(self, temp_xml_file_path): """Changes temporary xml file path to input temp_xml_file_path. This xml file is a duplicate of the phases_xml_file name and is @@ -1043,7 +1051,7 @@ self._temp_xml_file_path = temp_xml_file_path return None
-
[docs] def get_dependant_params_dict(self): +
[docs] def get_dependant_params_dict(self): """ Returns the dependant_params_dict @@ -1052,7 +1060,7 @@ """ return self._dependant_params_dict
-
[docs] def set_dependant_params_dict(self, dependant_params_dict): +
[docs] def set_dependant_params_dict(self, dependant_params_dict): """ Sets the dependant_params_dict @@ -1062,7 +1070,7 @@ self._dependant_params_dict = dependant_params_dict return None
-
[docs] def get_custom_objects_dict(self): +
[docs] def get_custom_objects_dict(self): """ Returns the custom_objects_dict @@ -1071,7 +1079,7 @@ """ return self._custom_objects_dict
-
[docs] def set_custom_objects_dict(self, custom_objects_dict): +
[docs] def set_custom_objects_dict(self, custom_objects_dict): """ Sets the custom_objects_dict @@ -1081,7 +1089,11 @@ self._custom_objects_dict = custom_objects_dict return None
-
[docs] def update_predicted_dict(self, + # TODO: Change DataFrame strucutre to contain info whether to set + # equilibrium pH to measured value. Will be useful for saponification + # TODO: Find way to add saponification to model. + # Maybe use fsolve to match experimental equilibrium pH +
[docs] def update_predicted_dict(self, phases_xml_filename=None, phase_names=None): """Function that computes the predicted equilibrium concentrations @@ -1148,7 +1160,7 @@ self._predicted_dict = predicted_dict return None
-
[docs] def get_predicted_dict(self): +
[docs] def get_predicted_dict(self): """Returns predicted dictionary of species concentrations that xml parameters predicts given current in_moles @@ -1212,7 +1224,7 @@ objective_values = objective_values[0] return objective_values -
[docs] def fit(self, +
[docs] def fit(self, objective_function=None, optimizer=None, objective_kwargs=None, @@ -1255,7 +1267,7 @@ return opt_dict, obj_value
-
[docs] def update_xml(self, +
[docs] def update_xml(self, info_dict, phases_xml_filename=None, dependant_params_dict=None): @@ -1362,7 +1374,7 @@ self.set_phases(self._phases_xml_filename, self._phase_names) return None
-
[docs] def update_custom_objects_dict(self, info_dict): +
[docs] def update_custom_objects_dict(self, info_dict): """ updates internal custom_objects_dict with info_dict @@ -1387,7 +1399,7 @@ self._custom_objects_dict = custom_objects_dict return None
-
[docs] def parity_plot(self, +
[docs] def parity_plot(self, compared_value=None, c_data=None, c_label=None, @@ -1496,7 +1508,9 @@ filtered_meas = filtered_data['meas'] filtered_pred = filtered_data['pred'] if len(filtered_pred) != 0: - ax.scatter(filtered_meas, filtered_pred, label=label) + ax.scatter(filtered_meas, + filtered_pred, + label=label) if legend: ax.legend(loc='best') @@ -1531,7 +1545,7 @@ plt.savefig(save_path, bbox_inches='tight') return fig, ax
-
[docs] def r_squared(self, compared_value=None): +
[docs] def r_squared(self, compared_value=None): """r-squared value comparing measured and predicted compared value Closer to 1, the better the model's predictions. @@ -1571,7 +1585,7 @@ r_2 = (1 - num / den) return r_2
-
[docs] @staticmethod +
[docs] @staticmethod def plot_3d_data(x_data, y_data, z_data, @@ -1581,6 +1595,8 @@ z_label=None, c_label=None): """ + THis is for plotting 3d scatter plots. + We suggest use matplotlib's ax.scatter to make 3d plots. :param x_data: (list) list of data for x axis :param y_data: (list) list of data for y axis @@ -1631,7 +1647,7 @@

- © Copyright 2020, Titus Quah, Nwike Iloeje + © Copyright 2020, UChicago Argonne, LLC.

diff --git a/docs/_build/html/_modules/reeps/reeps.html b/docs/_build/html/_modules/reeps/reeps.html deleted file mode 100644 index bb9a06e..0000000 --- a/docs/_build/html/_modules/reeps/reeps.html +++ /dev/null @@ -1,1735 +0,0 @@ - - - - - - - - - - - reeps.reeps — LLEPE 1.0.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- -

Source code for reeps.reeps

-from datetime import datetime
-import cantera as ct
-import pandas as pd
-import numpy as np
-from scipy.optimize import minimize
-# noinspection PyPep8Naming
-import xml.etree.ElementTree as ET
-import seaborn as sns
-import matplotlib.pyplot as plt
-import shutil
-import copy
-from inspect import signature
-import os
-import re
-import pkg_resources
-from .utils import set_size
-
-sns.set()
-sns.set(font_scale=1.6)
-
-
-
[docs]class REEPS: - r""" - Rare earth elements (REE or RE) Takes in experimental data - Returns parameters for GEM - - .. note:: - - The order in which the REEs appear in the csv file must be the same - order as they appear in the xml, complex_names and - rare_earth_ion_names. - - For example, say in exp_csv_filename's csv, RE_1 is Nd RE_2 is Pr, - and - - .. code-block:: python - - aq_solvent_name = 'H2O(L)' - extractant_name = '(HA)2(org)' - diluent_name = 'dodecane' - - Then: - - The csvs column ordering must be: - - [h_i, h_eq, z_i, z_eq, Nd_aq_i, Nd_aq_eq, Nd_d_eq, - Pr_aq_i, Pr_aq_eq, Pr_d_eq] - - The aqueous speciesArray must be - "H2O(L) H+ OH- Cl- Nd+++ Pr+++" - - The organic speciesArray must be - "(HA)2(org) dodecane Nd(H(A)2)3(org) Pr(H(A)2)3(org)" - - .. code-block:: python - - complex_names = ['Nd(H(A)2)3(org)', 'Pr(H(A)2)3(org)'] - rare_earth_ion_names = ['Nd+++', 'Pr+++'] - - - :param exp_data: (str) csv file name with experimental data - - In the .csv file, the rows are different experiments and - columns are the measured quantities. - - The ordering of the columns needs to be: - - [h_i, h_eq, z_i, z_eq, - {RE_1}_aq_i, {RE_1}_aq_eq, {RE_1}_d_eq, - {RE_2}_aq_i, {RE_2}_aq_eq, {RE_2}_d_eq,... - {RE_N}_aq_i, {RE_N}_aq_eq, {RE_N}_d_eq] - - Naming does not matter, just the order. - - Where {RE_1}-{RE_N} are the rare earth element names of interest - i.e. Nd, Pr, La, etc. - - Below is an explanation of the columns. - - +-------+------------+------------------------------------------+ - | Index | Column | Meaning | - +=======+============+==========================================+ - | 0 | h_i | Initial Concentration of | - | | | H+ ions (mol/L) | - +-------+------------+------------------------------------------+ - | 1 | h_eq | Equilibrium concentration of | - | | | H+ ions (mol/L) | - +-------+------------+------------------------------------------+ - | 2 | z_i | Initial concentration of | - | | | extractant (mol/L) | - +-------+------------+------------------------------------------+ - | 3 | z_eq | Equilibrium concentration of | - | | | extractant (mol/L) | - +-------+------------+------------------------------------------+ - | 4 | {RE}_aq_i | Initial concentration of RE ions (mol/L) | - +-------+------------+------------------------------------------+ - | 5 | {RE}_aq_eq | Equilibrium concentration of RE ions | - | | | in aqueous phase (mol/L) | - +-------+------------+------------------------------------------+ - | 6 | {RE}_d_eq | Equilibrium Ratio between amount of | - | | | RE atoms in organic to aqueous | - +-------+------------+------------------------------------------+ - :param phases_xml_filename: (str) xml file with parameters - for equilibrium calc - - Would recommend copying and modifying xmls located in data/xmls - or in Cantera's "data" folder - - speciesArray fields need specific ordering. - - In aqueous phase: aq_solvent_name, H+, OH-, Cl-, RE_1, RE_2, ..., RE_N - - For aqueous phase, RE_1-RE_N represent RE ion names i.e. Nd+++, Pr+++ - - In organic phase : extractant_name, diluant_name, RE_1, RE_2, ..., RE_N - - For organic phase, RE_1-RE_N represent RE complex names - i.e. Nd(H(A)2)3(org), Pr(H(A)2)3(org) - - :param phase_names: (list) names of phases in xml file - - Found in the xml file under <phase ... id={phase_name}> - - :param aq_solvent_name: (str) name of aqueous solvent in xml file - :param extractant_name: (str) name of extractant in xml file - :param diluant_name: (str) name of diluant in xml file - :param complex_names: (list) names of complexes in xml file. - - Ensure the ordering is correct - :param rare_earth_ion_names: (list) names of rare earth ions in xml file - - Ensure the ordering is correct - :param re_species_list: (list) names of rare earth elements. - - If ``None``, re_species_list will be rare_earth_ion_names without '+' - i.e. 'Nd+++'->'Nd' - - Ensure the ordering is correct - :param aq_solvent_rho: (float) density of solvent (g/L) - - If ``None``, molar volume/molecular weight is used from xml - :param extractant_rho: (float) density of extractant (g/L) - - If ``None``, molar volume/molecular weight is used from xml - :param diluant_rho: (float) density of diluant (g/L) - - If ``None``, molar volume/molecular weight is used from xml - :param opt_dict: (dict) dictionary containing info about which - species parameters are updated to fit model to experimental data - - Should have the format as below. Dictionary keys under user defined - parameter name must be named as shown below ('upper_element_name', - 'upper_attrib_name', etc.). 'attrib_name's and 'attrib_value's can - be None. {} denotes areas for user to fill in. - - .. code-block:: python - - opt_dict = {"{user_defined_name_for_parameter_1}": - {'upper_element_name': {param_upper_element}, - 'upper_attrib_name': {param_upper_attrib_name}, - 'upper_attrib_value': {param_upper_attrib_value}, - 'lower_element_name': {param_lower_element}, - 'lower_attrib_name': {param_lower_attrib_name}, - 'lower_attrib_value': {param_lower_attrib_value}, - 'input_format': {str format to input input_value} - 'input_value': {guess_value}}, - "{user_defined_name_for_parameter_2}": - ... - ... - } - :param objective_function: (function or str) function to compute objective - - By default, the objective function is log mean squared error - of distribution ratio - - .. code-block:: python - - np.sum((np.log10(d_pred)-np.log10(d_meas))^2) - - Function needs to take inputs: - - .. code-block:: python - - objective_function(predicted_dict, measured_df, kwargs) - - ``kwargs`` is optional - - Function needs to return: (float) value computed by objective function - - Below is the guide for referencing predicted values - - +---------------------------+--------------------------------+ - | To access | Use | - +===========================+================================+ - | hydrogen ion conc in aq | predicted_dict['h_eq'] | - +---------------------------+--------------------------------+ - | extractant conc in org | predicted_dict['z_eq'] | - +---------------------------+--------------------------------+ - | RE ion eq conc in aq | predicted_dict['{RE}_aq_eq'] | - +---------------------------+--------------------------------+ - | RE complex eq conc in org | predicted_dict['{RE}_org_eq'] | - +---------------------------+--------------------------------+ - | RE distribution ratio | predicted_dict['{RE}_d_eq'] | - +---------------------------+--------------------------------+ - - Replace "{RE}" with rare earth element i.e. Nd, La, etc. - - For measured values, use the same names, but - replace ``predicted_dict`` with ``measured_df`` - :param optimizer: (function or str) function to perform optimization - - .. note:: - - The optimized variables are not directly the species parameters, - but instead are first multiplied by the initial guess before - sending becoming the species parameters. - - For example, say - - .. code-block:: python - - opt_dict = {'Nd(H(A)2)3(org):'h0':-4.7e6} - - If the bounds on h0 need to be [-4.7e7,-4.7e5], then - divide the bounds by the guess and get - - .. code-block:: python - - "bounds": [(1e-1, 1e1)] - - By default, the optimizer is scipy's optimize function with - - .. code-block:: python - - default_kwargs= {"method": 'SLSQP', - "bounds": [(1e-1, 1e1)] * len(x_guess), - "constraints": (), - "options": {'disp': True, - 'maxiter': 1000, - 'ftol': 1e-6}} - - Function needs to take inputs: - ``optimizer(objective_function, x_guess, kwargs)`` - - ``kwargs`` is optional - - Function needs to return: ((np.ndarray, float)) Optimized parameters, - objective_function value - - :param temp_xml_file_path: (str) path to temporary xml file. - - This xml file is a duplicate of the phases_xml_file name and is - modified during the optimization process to avoid changing the original - xml file - - default is local temp folder - - :param dependant_params_dict: (dict) dictionary containing information - about parameters dependant on opt_dict - """ - - def __init__(self, - exp_data, - phases_xml_filename, - phase_names, - aq_solvent_name, - extractant_name, - diluant_name, - complex_names, - rare_earth_ion_names, - re_species_list=None, - aq_solvent_rho=None, - extractant_rho=None, - diluant_rho=None, - opt_dict=None, - objective_function='Log-MSE', - optimizer='SLSQP', - temp_xml_file_path=None, - dependant_params_dict=None, - ): - self._built_in_obj_list = ['Log-MSE'] - self._built_in_opt_list = ['SLSQP'] - self._exp_data = exp_data - self._phases_xml_filename = phases_xml_filename - self._opt_dict = opt_dict - self._phase_names = phase_names - self._aq_solvent_name = aq_solvent_name - self._extractant_name = extractant_name - self._diluant_name = diluant_name - self._complex_names = complex_names - self._rare_earth_ion_names = rare_earth_ion_names - self._aq_solvent_rho = aq_solvent_rho - self._extractant_rho = extractant_rho - self._diluant_rho = diluant_rho - self._objective_function = None - self.set_objective_function(objective_function) - self._optimizer = None - self._re_species_list = re_species_list - self.set_optimizer(optimizer) - if temp_xml_file_path is None: - temp_xml_file_path = r'{0}/temp.xml'.format(os.getenv('TEMP')) - self._temp_xml_file_path = temp_xml_file_path - self._dependant_params_dict = dependant_params_dict - # Try and except for adding package data to path. - # This only works for sdist, not bdist - # If bdist is needed, research "manifest.in" python setup files - try: - shutil.copyfile(self._phases_xml_filename, - self._temp_xml_file_path) - self._phases = ct.import_phases(self._phases_xml_filename, - phase_names) - except FileNotFoundError: - self._phases_xml_filename = \ - pkg_resources.resource_filename('reeps', - r'..\data\xmls\{0}'.format( - phases_xml_filename)) - shutil.copyfile(self._phases_xml_filename, - self._temp_xml_file_path) - self._phases = ct.import_phases(self._phases_xml_filename, - phase_names) - if isinstance(self._exp_data, str): - try: - self._exp_df = pd.read_csv(self._exp_data) - except FileNotFoundError: - self._exp_data = pkg_resources.resource_filename( - 'reeps', r'..\data\csvs\{0}'.format(self._exp_data)) - self._exp_df = pd.read_csv(self._exp_data) - else: - self._exp_df = self._exp_data.copy() - - self._exp_df_columns = ['h_i', 'h_eq', 'z_i', 'z_eq'] - if self._re_species_list is None: - self._re_species_list = [] - for name in self._rare_earth_ion_names: - species = name.replace('+', '') - self._re_species_list.append(species) - for species in self._re_species_list: - self._exp_df_columns.append('{0}_aq_i'.format(species)) - self._exp_df_columns.append('{0}_aq_eq'.format(species)) - self._exp_df_columns.append('{0}_d_eq'.format(species)) - - self._exp_df.columns = self._exp_df_columns - for species in self._re_species_list: - self._exp_df['{0}_org_eq'.format(species)] = \ - self._exp_df['{0}_aq_eq'.format(species)] \ - * self._exp_df['{0}_d_eq'.format(species)] - - self._in_moles = None - - self._aq_ind = None - self._org_ind = None - self._re_charges = None - - self.set_in_moles(feed_vol=1) - self._predicted_dict = None - self.update_predicted_dict() - -
[docs] @staticmethod - def scipy_minimize(objective, x_guess, optimizer_kwargs=None): - """ The default optimizer for REEPS - - Uses scipy.minimize - - By default, options are - - .. code-block:: python - - default_kwargs= {"method": 'SLSQP', - "bounds": [(1e-1, 1e1)]*len(x_guess), - "constraints": (), - "options": {'disp': True, - 'maxiter': 1000, - 'ftol': 1e-6}} - - :param objective: (func) the objective function - :param x_guess: (np.ndarray) the initial guess (always 1) - :param optimizer_kwargs: (dict) dictionary of options for minimize - :returns: ((np.ndarray, float)) Optimized parameters, - objective_function value - """ - if optimizer_kwargs is None: - optimizer_kwargs = {"method": 'SLSQP', - "bounds": [(1e-1, 1e1)] * len(x_guess), - "constraints": (), - "options": {'disp': True, - 'maxiter': 1000, - 'ftol': 1e-6}} - res = minimize(objective, x_guess, **optimizer_kwargs) - est_parameters = res.x - return est_parameters, res.fun
- -
[docs] def log_mean_squared_error(self, predicted_dict, meas_df): - """Default objective function for REEPS - - Returns the log mean squared error of - predicted distribution ratios (d=n_org/n_aq) - to measured d. - - np.sum((np.log10(d_pred)-np.log10(d_meas))\**2) - - :param predicted_dict: (dict) contains predicted data - :param meas_df: (pd.DataFrame) contains experimental data - :return: (float) log mean squared error between predicted and measured - """ - meas = np.concatenate([meas_df['{0}_d_eq'.format(species)].values - for species in self._re_species_list]) - pred = np.concatenate([ - predicted_dict['{0}_d_eq'.format(species)] - for species in self._re_species_list]) - log_pred = np.log10(pred) - log_meas = np.log10(meas) - log_diff = (log_pred - log_meas) ** 2 - obj = np.sum(log_diff) - return obj
- -
[docs] def get_exp_df(self) -> pd.DataFrame: - """Returns the experimental DataFrame - - :return: (pd.DataFrame) Experimental data - """ - return self._exp_df
- -
[docs] def set_exp_df(self, exp_data): - """Changes the experimental DataFrame to input exp_csv_filename data - and renames columns to internal REEPS names - - - h_i, h_eq, z_i, z_eq, {RE}_aq_i, {RE}_aq_eq, {RE}_d - - See class docstring on "exp_csv_filename" for further explanations. - - :param exp_data: (str or pd.DataFrame) - file name/path or DataFrame for experimental data csv - """ - self._exp_data = exp_data - if isinstance(self._exp_data, str): - try: - self._exp_df = pd.read_csv(self._exp_data) - except FileNotFoundError: - self._exp_data = pkg_resources.resource_filename( - 'reeps', r'..\data\csvs\{0}'.format(self._exp_data)) - self._exp_df = pd.read_csv(self._exp_data) - else: - self._exp_df = exp_data.copy() - self._exp_df_columns = ['h_i', 'h_eq', 'z_i', 'z_eq'] - if self._re_species_list is None: - self._re_species_list = [] - for name in self._rare_earth_ion_names: - species = name.replace('+', '') - self._re_species_list.append(species) - for species in self._re_species_list: - self._exp_df_columns.append('{0}_aq_i'.format(species)) - self._exp_df_columns.append('{0}_aq_eq'.format(species)) - self._exp_df_columns.append('{0}_d_eq'.format(species)) - self._exp_df.columns = self._exp_df_columns - for species in self._re_species_list: - self._exp_df['{0}_org_eq'.format(species)] = \ - self._exp_df['{0}_aq_eq'.format(species)] \ - * self._exp_df['{0}_d_eq'.format(species)] - self.set_in_moles(feed_vol=1) - self.update_predicted_dict() - return None
- -
[docs] def get_phases(self) -> list: - """ - Returns the list of Cantera solutions - - :return: (list) list of Cantera solutions/phases - """ - return self._phases
- -
[docs] def set_phases(self, phases_xml_filename, phase_names): - """Change list of Cantera solutions by inputting - new xml file name and phase names - - Also runs set_in_moles to set feed volume to 1 L - - :param phases_xml_filename: (str) xml file with parameters - for equilibrium calc - :param phase_names: (list) names of phases in xml file - """ - self._phases_xml_filename = phases_xml_filename - self._phase_names = phase_names - # Try and except for adding package data to path. - # This only works for sdist, not bdist - # If bdist is needed, research "manifest.in" python setup files - try: - shutil.copyfile(self._phases_xml_filename, - self._temp_xml_file_path) - self._phases = ct.import_phases(self._phases_xml_filename, - phase_names) - except FileNotFoundError: - self._phases_xml_filename = \ - pkg_resources.resource_filename('reeps', - r'..\data\xmls\{0}'.format( - phases_xml_filename)) - shutil.copyfile(self._phases_xml_filename, - self._temp_xml_file_path) - self._phases = ct.import_phases(self._phases_xml_filename, - phase_names) - self.set_in_moles(feed_vol=1) - self.update_predicted_dict() - return None
- -
[docs] def get_opt_dict(self) -> dict: - """ - Returns the dictionary containing optimization information - - :return: (dict) dictionary containing info about which - species parameters are updated to fit model to experimental data - """ - return self._opt_dict
- -
[docs] def set_opt_dict(self, opt_dict): - """ - Change the dictionary to input opt_dict. - - opt_dict specifies species parameters to be updated to - fit model to data - - See class docstring on "opt_dict" for more information. - - :param opt_dict: (dict) dictionary containing info about which - species parameters are updated to fit model to experimental data - """ - - self._opt_dict = opt_dict - return None
- -
[docs] def get_aq_solvent_name(self) -> str: - """Returns aq_solvent_name - - :return: aq_solvent_name: (str) name of aqueous solvent in xml file - """ - return self._aq_solvent_name
- -
[docs] def set_aq_solvent_name(self, aq_solvent_name): - """ Change aq_solvent_name to input aq_solvent_name - - :param aq_solvent_name: (str) name of aqueous solvent in xml file - """ - self._aq_solvent_name = aq_solvent_name - return None
- -
[docs] def get_extractant_name(self) -> str: - """Returns extractant name - - :return: extractant_name: (str) name of extractant in xml file - """ - return self._extractant_name
- -
[docs] def set_extractant_name(self, extractant_name): - """ - Change extractant_name to input extractant_name - :param extractant_name: (str) name of extractant in xml file - """ - self._extractant_name = extractant_name - return None
- -
[docs] def get_diluant_name(self) -> str: - """ Returns diluant name - :return: diluant_name: (str) name of diluant in xml file - """ - return self._diluant_name
- -
[docs] def set_diluant_name(self, diluant_name): - """ - Change diluant_name to input diluant_name - - :param diluant_name: (str) name of diluant in xml file - """ - self._diluant_name = diluant_name - return None
- -
[docs] def get_complex_names(self) -> list: - """Returns list of complex names - - :return: complex_names: (list) names of complexes in xml file. - """ - return self._complex_names
- -
[docs] def set_complex_names(self, complex_names): - """Change complex names list to input complex_names - - :param complex_names: (list) names of complexes in xml file. - """ - self._complex_names = complex_names - return None
- -
[docs] def get_rare_earth_ion_names(self) -> list: - """Returns list of rare earth ion names - - :return: rare_earth_ion_names: (list) names of rare earth ions in - xml file - """ - return self._rare_earth_ion_names
- -
[docs] def set_rare_earth_ion_names(self, rare_earth_ion_names): - """Change list of rare earth ion names to input - rare_earth_ion_names - - :param rare_earth_ion_names: (list) names of rare earth ions in - xml file - """ - self._rare_earth_ion_names = rare_earth_ion_names - return None
- -
[docs] def get_re_species_list(self) -> list: - """Returns list of rare earth element names - - :return: re_species_list: (list) names of rare earth elements in - xml file - """ - return self._re_species_list
- -
[docs] def set_re_species_list(self, re_species_list): - """Change list of rare earth ion names to input - rare_earth_ion_names - - :param re_species_list: (list) names of rare earth elements in - xml file - """ - self._re_species_list = re_species_list - return None
- -
[docs] def get_aq_solvent_rho(self) -> str: - """Returns aqueous solvent density (g/L) - - :return: aq_solvent_rho: (float) density of aqueous solvent - """ - return self._aq_solvent_rho
- -
[docs] def set_aq_solvent_rho(self, aq_solvent_rho): - """Changes aqueous solvent density (g/L) to input aq_solvent_rho - - :param aq_solvent_rho: (float) density of aqueous solvent - """ - self._aq_solvent_rho = aq_solvent_rho - return None
- -
[docs] def get_extractant_rho(self) -> str: - """Returns extractant density (g/L) - - :return: extractant_rho: (float) density of extractant - """ - return self._extractant_rho
- -
[docs] def set_extractant_rho(self, extractant_rho): - """Changes extractant density (g/L) to input extractant_rho - - :param extractant_rho: (float) density of extractant - """ - self._extractant_rho = extractant_rho - return None
- -
[docs] def get_diluant_rho(self) -> str: - """Returns diluant density (g/L) - - :return: diluant_rho: (float) density of diluant - """ - return self._diluant_rho
- -
[docs] def set_diluant_rho(self, diluant_rho): - """Changes diluant density (g/L) to input diluant_rho - - :param diluant_rho: (float) density of diluant - """ - self._diluant_rho = diluant_rho - return None
- -
[docs] def set_in_moles(self, feed_vol): - """Function that initializes mole fractions to input feed_vol - - This function is called at initialization - - Sets in_moles to a pd.DataFrame containing initial mole fractions - - Columns for species and rows for different experiments - - This function also calls update_predicted_dict - - :param feed_vol: (float) feed volume of mixture (L) - """ - phases_copy = self._phases.copy() - exp_df = self._exp_df.copy() - solvent_name = self._aq_solvent_name - extractant_name = self._extractant_name - diluant_name = self._diluant_name - solvent_rho = self._aq_solvent_rho - extractant_rho = self._extractant_rho - diluant_rho = self._diluant_rho - re_names = self._rare_earth_ion_names - re_species_list = self._re_species_list - - mixed = ct.Mixture(phases_copy) - aq_ind = None - solvent_ind = None - for ind, phase in enumerate(phases_copy): - if solvent_name in phase.species_names: - aq_ind = ind - solvent_ind = phase.species_names.index(solvent_name) - if aq_ind is None: - raise Exception('Solvent "{0}" not found \ - in xml file'.format(solvent_name)) - - if aq_ind == 0: - org_ind = 1 - else: - org_ind = 0 - self._aq_ind = aq_ind - self._org_ind = org_ind - extractant_ind = phases_copy[org_ind].species_names.index( - extractant_name) - diluant_ind = phases_copy[org_ind].species_names.index(diluant_name) - - re_ind_list = [phases_copy[aq_ind].species_names.index(re_name) - for re_name in re_names] - re_charges = np.array([phases_copy[aq_ind].species(re_ind).charge - for re_ind in re_ind_list]) - self._re_charges = re_charges - - mix_aq = mixed.phase(aq_ind) - mix_org = mixed.phase(org_ind) - solvent_mw = mix_aq.molecular_weights[solvent_ind] # g/mol - extractant_mw = mix_org.molecular_weights[extractant_ind] - diluant_mw = mix_org.molecular_weights[diluant_ind] - if solvent_rho is None: - solvent_rho = mix_aq(aq_ind).partial_molar_volumes[ - solvent_ind] / solvent_mw * 1e6 # g/L - self._aq_solvent_rho = solvent_rho - if extractant_rho is None: - extractant_rho = mix_org(org_ind).partial_molar_volumes[ - extractant_ind] / extractant_mw * 1e6 - self._extractant_rho = extractant_rho - if diluant_rho is None: - diluant_rho = mix_org(org_ind).partial_molar_volumes[ - extractant_ind] / extractant_mw * 1e6 - self._diluant_rho = diluant_rho - - in_moles_data = [] - aq_phase_solvent_moles = feed_vol * solvent_rho / solvent_mw - for index, row in exp_df.iterrows(): - h_plus_moles = feed_vol * row['h_i'] - hydroxide_ions = 0 - rare_earth_moles = np.array([feed_vol * row[ - '{0}_aq_i'.format(re_species)] - for re_species in re_species_list]) - re_charge_sum = np.sum(re_charges * rare_earth_moles) - chlorine_moles = re_charge_sum + h_plus_moles - extractant_moles = feed_vol * row['z_i'] - extractant_vol = extractant_moles * extractant_mw / extractant_rho - diluant_vol = feed_vol - extractant_vol - diluant_moles = diluant_vol * diluant_rho / diluant_mw - complex_moles = np.zeros(len(re_species_list)) - - species_moles_aq = [aq_phase_solvent_moles, - h_plus_moles, - hydroxide_ions, - chlorine_moles] - species_moles_aq.extend(list(rare_earth_moles)) - species_moles_org = [extractant_moles, diluant_moles] - species_moles_org.extend(list(complex_moles)) - if aq_ind == 0: - species_moles = species_moles_aq + species_moles_org - else: - species_moles = species_moles_org + species_moles_aq - in_moles_data.append(species_moles) - self._in_moles = pd.DataFrame( - in_moles_data, columns=mixed.species_names) - self.update_predicted_dict() - return None
- -
[docs] def get_in_moles(self) -> pd.DataFrame: - """Returns the in_moles DataFrame which contains the initial mole - fractions of each species for each experiment - - :return: in_moles: (pd.DataFrame) DataFrame with initial mole fractions - """ - return self._in_moles
- -
[docs] def set_objective_function(self, objective_function): - """Change objective function to input objective_function. - - See class docstring on "objective_function" for instructions - - :param objective_function: (func) Objective function to quantify - error between model and experimental data - """ - if not callable(objective_function) \ - and objective_function not in self._built_in_obj_list: - raise Exception( - "objective_function must be a function " - "or in this strings list: {0}".format( - self._built_in_obj_list)) - if callable(objective_function): - if len(signature(objective_function).parameters) < 2: - raise Exception( - "objective_function must be a function " - "with at least 3 arguments:" - " f(predicted_dict, experimental_df, kwargs)") - if objective_function == 'Log-MSE': - objective_function = self.log_mean_squared_error - self._objective_function = objective_function - return None
- -
[docs] def get_objective_function(self): - """Returns objective function - - :return: objective_function: (func) Objective function to quantify - error between model and experimental data - """ - return self._objective_function
- -
[docs] def set_optimizer(self, optimizer): - """Change optimizer function to input optimizer. - - See class docstring on "optimizer" for instructions - - :param optimizer: (func) Optimizer function to minimize objective - function - """ - if not callable(optimizer) \ - and optimizer not in self._built_in_opt_list: - raise Exception( - "optimizer must be a function " - "or in this strings list: {0}".format( - self._built_in_opt_list)) - if callable(optimizer): - if len(signature(optimizer).parameters) < 2: - raise Exception( - "optimizer must be a function " - "with at least 2 arguments: " - "f(objective_func,x_guess, kwargs)") - if optimizer == 'SLSQP': - optimizer = self.scipy_minimize - self._optimizer = optimizer - return None
- -
[docs] def get_optimizer(self): - """Returns objective function - - :return: optimizer: (func) Optimizer function to minimize objective - function - """ - return self._optimizer
- -
[docs] def get_temp_xml_file_path(self): - """Returns path to temporary xml file. - - This xml file is a duplicate of the phases_xml_file name and is - modified during the optimization process to avoid changing the original - xml file. - - :return: temp_xml_file_path: (str) path to temporary xml file. - """ - return self._temp_xml_file_path
- -
[docs] def set_temp_xml_file_path(self, temp_xml_file_path): - """Changes temporary xml file path to input temp_xml_file_path. - - This xml file is a duplicate of the phases_xml_file name and is - modified during the optimization process to avoid changing the original - xml file. - - :param temp_xml_file_path: (str) path to temporary xml file. - """ - self._temp_xml_file_path = temp_xml_file_path - return None
- -
[docs] def get_dependant_params_dict(self): - """ - Returns the dependant_params_dict - :return: dependant_params_dict: (dict) dictionary containing - information about parameters dependant on opt_dict - """ - return self._dependant_params_dict
- -
[docs] def set_dependant_params_dict(self, dependant_params_dict): - """ - Sets the dependant_params_dict - :param dependant_params_dict: (dict) dictionary containing information - about parameters dependant on opt_dict - """ - self._dependant_params_dict = dependant_params_dict - return None
- -
[docs] def update_predicted_dict(self, - phases_xml_filename=None, - phase_names=None): - """Function that computes the predicted equilibrium concentrations - the fed phases_xml_filename parameters predicts given the initial - mole fractions set by in_moles() - - :param phases_xml_filename: (str)xml file with parameters - for equilibrium calc. If ``None``, the - current phases_xml_filename is used. - :param phase_names: (list) names of phases in xml file. - If ``None``, the current phases_names is used. - """ - if phases_xml_filename is None: - phases_xml_filename = self._phases_xml_filename - if phase_names is None: - phase_names = self._phase_names - aq_ind = self._aq_ind - org_ind = self._org_ind - complex_names = self._complex_names - extractant_name = self._extractant_name - rare_earth_ion_names = self._rare_earth_ion_names - in_moles = self._in_moles - re_species_list = self._re_species_list - - phases_copy = ct.import_phases(phases_xml_filename, phase_names) - mix = ct.Mixture(phases_copy) - key_names = ['h_eq', 'z_eq'] - for re_species in re_species_list: - key_names.append('{0}_aq_eq'.format(re_species)) - key_names.append('{0}_org_eq'.format(re_species)) - key_names.append('{0}_d_eq'.format(re_species)) - - predicted_dict = {'{0}'.format(key_name): [] - for key_name in key_names} - - for row in in_moles.values: - mix.species_moles = row - mix.equilibrate('TP', log_level=0) - re_org_array = np.array([mix.species_moles[mix.species_index( - org_ind, complex_name)] for complex_name in complex_names]) - re_aq_array = np.array([mix.species_moles[mix.species_index( - aq_ind, re_ion_name)] for re_ion_name in rare_earth_ion_names]) - d_array = re_org_array / re_aq_array - hydrogen_ions = mix.species_moles[mix.species_index(aq_ind, 'H+')] - extractant = mix.species_moles[mix.species_index( - org_ind, extractant_name)] - for index, re_species in enumerate(re_species_list): - predicted_dict['{0}_aq_eq'.format( - re_species)].append(re_aq_array[index]) - predicted_dict['{0}_org_eq'.format( - re_species)].append(re_org_array[index]) - predicted_dict['{0}_d_eq'.format( - re_species)].append(d_array[index]) - predicted_dict['h_eq'].append(hydrogen_ions) - predicted_dict['z_eq'].append(extractant) - for key, value in predicted_dict.items(): - predicted_dict[key] = np.array(value) - self._predicted_dict = predicted_dict - return None
- -
[docs] def get_predicted_dict(self): - """Returns predicted dictionary of species concentrations - that xml parameters predicts given current in_moles - - :return: predicted_dict: (dict) dictionary of species concentrations - """ - return self._predicted_dict
- - def _internal_objective(self, x, kwargs=None): - """ - Internal objective function. Uses objective function to compute value - If the optimizer requires vectorized variables ie pso, this function - takes care of it - - :param x: (list) thermo properties varied to minimize objective func - :param kwargs: (list) arguments for objective_function - """ - temp_xml_file_path = self._temp_xml_file_path - exp_df = self._exp_df - objective_function = self._objective_function - opt_dict = copy.deepcopy(self._opt_dict) - dep_dict = copy.deepcopy(self._dependant_params_dict) - x = np.array(x) - - if len(x.shape) == 1: - xs = np.array([x]) - vectorized_x = False - else: - vectorized_x = True - xs = x - objective_values = [] - for x in xs: - i = 0 - for species_name in opt_dict.keys(): - for thermo_prop in opt_dict[species_name].keys(): - if not np.isnan( - x[i]): # if nan, do not update xml with nan - opt_dict[species_name][thermo_prop] *= x[i] - i += 1 - - self.update_xml(opt_dict, - temp_xml_file_path, - dependant_params_dict=dep_dict) - - self.update_predicted_dict(temp_xml_file_path) - predicted_dict = self.get_predicted_dict() - self.update_predicted_dict() - - if kwargs is None: - # noinspection PyCallingNonCallable - obj = objective_function(predicted_dict, exp_df) - else: - # noinspection PyCallingNonCallable - obj = objective_function(predicted_dict, exp_df, **kwargs) - objective_values.append(obj) - if vectorized_x: - objective_values = np.array(objective_values) - else: - objective_values = objective_values[0] - return objective_values - -
[docs] def fit(self, - objective_function=None, - optimizer=None, - objective_kwargs=None, - optimizer_kwargs=None) -> tuple: - """Fits experimental to modeled data by minimizing objective function - with optimizer. Returns dictionary with opt_dict structure - - :param objective_function: (function) function to compute objective - If 'None', last set objective or default function is used - :param optimizer: (function) function to perform optimization - If 'None', last set optimizer or default is used - :param optimizer_kwargs: (dict) optional arguments for optimizer - :param objective_kwargs: (dict) optional arguments - for objective function - :returns tuple: (opt_dict (dict), opt_value (float)) - optimized opt_dict: Has identical structure as opt_dict - """ - if objective_function is not None: - self.set_objective_function(objective_function) - if optimizer is not None: - self.set_optimizer(optimizer) - - def objective(x): - return self._internal_objective(x, objective_kwargs) - - optimizer = self._optimizer - opt_dict = copy.deepcopy(self._opt_dict) - i = 0 - for species_name in opt_dict.keys(): - for _ in opt_dict[species_name].keys(): - i += 1 - x_guess = np.ones(i) - - if optimizer_kwargs is None: - # noinspection PyCallingNonCallable - est_parameters, obj_value = optimizer(objective, x_guess) - else: - # noinspection PyCallingNonCallable - est_parameters, obj_value = optimizer(objective, - x_guess, - optimizer_kwargs) - - i = 0 - for species_name in opt_dict.keys(): - for thermo_prop in opt_dict[species_name].keys(): - opt_dict[species_name][thermo_prop] *= est_parameters[i] - i += 1 - return opt_dict, obj_value
- -
[docs] def update_xml(self, - info_dict, - phases_xml_filename=None, - dependant_params_dict=None): - """updates xml file with info_dict - - :param info_dict: (dict) info in {species_names:{thermo_prop:val}} - Requires an identical structure to opt_dict - :param phases_xml_filename: (str) xml filename if editing other xml - If ``None``, the current xml will be modified and the internal - Cantera phases will be refreshed to the new values. - :param dependant_params_dict: (dict) dictionary containing information - about parameters dependant on info_dict - """ - if phases_xml_filename is None: - phases_xml_filename = self._phases_xml_filename - new_dict = copy.deepcopy(info_dict) - dep_dict = dependant_params_dict - if dep_dict is not None: - for species_name in dep_dict.keys(): - for thermo_prop in dep_dict[species_name]: - mod_func = \ - dep_dict[species_name][thermo_prop]['function'] - mod_kwargs = \ - dep_dict[species_name][thermo_prop]['kwargs'] - ind_vars = \ - dep_dict[species_name][thermo_prop]['ind_vars'] - ind_vals = [new_dict[ind_var[0]][ind_var[1]] - for ind_var in ind_vars] - - new_dict[species_name] = {} - new_dict[species_name][thermo_prop] = {} - new_dict[species_name][thermo_prop] = \ - mod_func(ind_vals, **mod_kwargs) - # print(mod_func(ind_vals, **mod_kwargs)) - # print(new_dict) - - tree = ET.parse(phases_xml_filename) - root = tree.getroot() - # Update xml file - for species_name in new_dict.keys(): - for thermo_prop in new_dict[species_name].keys(): - for species in root.iter('species'): - if species.attrib['name'] == species_name: - for changed_prop in species.iter(thermo_prop): - changed_prop.text = str( - new_dict[species_name][thermo_prop]) - now = datetime.now() - changed_prop.set('updated', - 'Updated at {0}:{1} {2}-{3}-{4}' - .format(now.hour, now.minute, - now.month, now.day, - now.year)) - - tree.write(phases_xml_filename) - if phases_xml_filename == self._phases_xml_filename: - self.set_phases(self._phases_xml_filename, self._phase_names) - return None
- - def _internal_objective_ver2(self, x, kwargs=None): - """ - ver2 generalizes to handle accessing parameters. ver1 assumes species - parameter is modified. ver2 assumes parameter is accessed by going - through two levels: upper and lower - Internal objective function. Uses objective function to compute value - If the optimizer requires vectorized variables ie pso, this function - takes care of it - - :param x: (list) thermo properties varied to minimize objective func - :param kwargs: (list) arguments for objective_function - """ - temp_xml_file_path = self._temp_xml_file_path - exp_df = self._exp_df - objective_function = self._objective_function - opt_dict = copy.deepcopy(self._opt_dict) - dep_dict = copy.deepcopy(self._dependant_params_dict) - x = np.array(x) - - if len(x.shape) == 1: - xs = np.array([x]) - vectorized_x = False - else: - vectorized_x = True - xs = x - objective_values = [] - for x in xs: - for ind, param_name in enumerate(opt_dict.keys()): - if not np.isnan( - x[ind]): # if nan, do not update xml with nan - opt_dict[param_name]['input_value'] *= x[ind] - - self.update_xml_ver2(opt_dict, - temp_xml_file_path, - dependant_params_dict=dep_dict) - - self.update_predicted_dict(temp_xml_file_path) - predicted_dict = self.get_predicted_dict() - self.update_predicted_dict() - - if kwargs is None: - # noinspection PyCallingNonCallable - obj = objective_function(predicted_dict, exp_df) - else: - # noinspection PyCallingNonCallable - obj = objective_function(predicted_dict, exp_df, **kwargs) - objective_values.append(obj) - if vectorized_x: - objective_values = np.array(objective_values) - else: - objective_values = objective_values[0] - return objective_values - -
[docs] def fit_ver2(self, - objective_function=None, - optimizer=None, - objective_kwargs=None, - optimizer_kwargs=None) -> tuple: - """Fits experimental to modeled data by minimizing objective function - with optimizer. Returns dictionary with opt_dict structure - - :param objective_function: (function) function to compute objective - If 'None', last set objective or default function is used - :param optimizer: (function) function to perform optimization - If 'None', last set optimizer or default is used - :param optimizer_kwargs: (dict) optional arguments for optimizer - :param objective_kwargs: (dict) optional arguments - for objective function - :returns tuple: (opt_dict (dict), opt_value (float)) - optimized opt_dict: Has identical structure as opt_dict - """ - if objective_function is not None: - self.set_objective_function(objective_function) - if optimizer is not None: - self.set_optimizer(optimizer) - - def objective(x): - return self._internal_objective_ver2(x, objective_kwargs) - - optimizer = self._optimizer - opt_dict = copy.deepcopy(self._opt_dict) - x_guess = np.ones(len(list(opt_dict.keys()))) - - if optimizer_kwargs is None: - # noinspection PyCallingNonCallable - est_parameters, obj_value = optimizer(objective, x_guess) - else: - # noinspection PyCallingNonCallable - est_parameters, obj_value = optimizer(objective, - x_guess, - optimizer_kwargs) - for ind, param_name in enumerate(opt_dict.keys()): - opt_dict[param_name]['input_value'] *= est_parameters[ind] - - return opt_dict, obj_value
- -
[docs] def update_xml_ver2(self, - info_dict, - phases_xml_filename=None, - dependant_params_dict=None): - """updates xml file with info_dict - - :param info_dict: (dict) info in {species_names:{thermo_prop:val}} - Requires an identical structure to opt_dict - :param phases_xml_filename: (str) xml filename if editing other xml - If ``None``, the current xml will be modified and the internal - Cantera phases will be refreshed to the new values. - :param dependant_params_dict: (dict) dictionary containing information - about parameters dependant on info_dict - """ - if phases_xml_filename is None: - phases_xml_filename = self._phases_xml_filename - new_dict = copy.deepcopy(info_dict) - dep_dict = dependant_params_dict - - if dep_dict is not None: - new_dict.update(dep_dict) - for param_name in dep_dict.keys(): - mod_func = \ - dep_dict[param_name]['function'] - mod_kwargs = \ - dep_dict[param_name]['kwargs'] - if isinstance(dep_dict[param_name]['independent_params'], str): - ind_param_names = [dep_dict[ - param_name]['independent_params']] - else: - ind_param_names = \ - dep_dict[param_name]['independent_params'] - ind_vals = [new_dict[ind_param_name]['input_value'] - for ind_param_name in ind_param_names] - if mod_kwargs is None: - new_dict[param_name]['input_value'] = mod_func(ind_vals) - else: - new_dict[param_name]['input_value'] = \ - mod_func(ind_vals, - **mod_kwargs) - tree = ET.parse(phases_xml_filename) - root = tree.getroot() - # Update xml file - for key in list(new_dict.keys()): - d = new_dict[key] - now = datetime.now() - if (d['upper_attrib_name'] is not None - and d['lower_attrib_name'] is not None): - for child1 in root.iter(d['upper_element_name']): - if (child1.attrib[d['upper_attrib_name']] - == d['upper_attrib_value']): - for child2 in child1.iter(d['lower_element_name']): - if (child1.attrib[d['lower_attrib_name']] - == d['lower_attrib_value']): - child2.text = d['input_format'].format( - d['input_value']) - child2.set('updated', - 'Updated at {0}:{1} {2}-{3}-{4}' - .format(now.hour, now.minute, - now.month, now.day, - now.year)) - elif (d['upper_attrib_name'] is None - and d['lower_attrib_name'] is not None): - for child1 in root.iter(d['upper_element_name']): - for child2 in child1.iter(d['lower_element_name']): - if (child1.attrib[d['lower_attrib_name']] - == d['lower_attrib_value']): - child2.text = d['input_format'].format( - d['input_value']) - child2.set('updated', - 'Updated at {0}:{1} {2}-{3}-{4}' - .format(now.hour, now.minute, - now.month, now.day, - now.year)) - elif (d['upper_attrib_name'] is not None - and d['lower_attrib_name'] is None): - for child1 in root.iter(d['upper_element_name']): - if (child1.attrib[d['upper_attrib_name']] - == d['upper_attrib_value']): - for child2 in child1.iter(d['lower_element_name']): - child2.text = d['input_format'].format( - d['input_value']) - child2.set('updated', - 'Updated at {0}:{1} {2}-{3}-{4}' - .format(now.hour, now.minute, - now.month, now.day, - now.year)) - else: - for child1 in root.iter(d['upper_element_name']): - for child2 in child1.iter(d['lower_element_name']): - child2.text = d['input_format'].format( - d['input_value']) - child2.set('updated', 'Updated at {0}:{1} {2}-{3}-{4}' - .format(now.hour, now.minute, - now.month, now.day, - now.year)) - - tree.write(phases_xml_filename) - if phases_xml_filename == self._phases_xml_filename: - self.set_phases(self._phases_xml_filename, self._phase_names) - return None
- -
[docs] def parity_plot(self, - compared_value=None, - c_data=None, - c_label=None, - plot_title=None, - save_path=None, - print_r_squared=False, - data_labels=None, - legend=True): - """ - Parity plot between measured and predicted compared_value. - Default compared value is {RE_1}_aq_eq - - :param compared_value: (str) Quantity to compare predicted and - experimental data. Can be any column containing "eq" in exp_df i.e. - h_eq, z_eq, {RE}_d_eq, etc. - :param plot_title: (str or boolean) - - If None (default): Plot title will be generated from compared_value - Recommend to just explore. If h_eq, plot_title is - "H^+ eq conc". - - If str: Plot title will be plot_title string - - If "False": No plot title - :param c_data: (list or np.ndarray) data for color axis - :param c_label: (str) label for color axis - :param save_path: (str) save path for parity plot - :param print_r_squared: (boolean) To plot or not to plot r-squared - value. Prints 2 places past decimal - :param data_labels: labels for the data such as paper's name where - experiment is pulled from. - :param legend: whether to display legend for data_labels. Has no - effect if data_labels is None - :return fig, ax: returns the figure and axes objects - """ - exp_df = self.get_exp_df() - predicted_dict = self.get_predicted_dict() - re_species_list = self._re_species_list - extractant_name = self.get_extractant_name() - re_charges = self._re_charges - if compared_value is None: - compared_value = '{0}_aq_eq'.format(re_species_list[0]) - pred = pd.DataFrame(predicted_dict)[compared_value].fillna(0).values - meas = exp_df[compared_value].fillna(0).values - name_breakdown = re.findall('[^_\W]+', compared_value) - compared_species = name_breakdown[0] - if compared_species == 'h': - feed_molarity = exp_df['h_i'].fillna(0).values - elif compared_species == 'z': - feed_molarity = exp_df['z_i'].fillna(0).values - else: - feed_molarity = exp_df[ - '{0}_aq_i'.format(compared_species)].fillna(0).values - if isinstance(data_labels, list): - combined_df = pd.DataFrame({'pred': pred, - 'meas': meas, - 'label': data_labels, - 'feed_molarity': feed_molarity}) - elif isinstance(c_data, str): - combined_df = pd.DataFrame({'pred': pred, - 'meas': meas, - c_data: exp_df[c_data].values, - 'feed_molarity': feed_molarity}) - else: - combined_df = pd.DataFrame({'pred': pred, - 'meas': meas, - 'feed_molarity': feed_molarity}) - - combined_df = combined_df[(combined_df['feed_molarity'] != 0)] - meas = combined_df['meas'].values - pred = combined_df['pred'].values - - min_data = np.min([pred, meas]) - max_data = np.max([pred, meas]) - min_max_data = np.array([min_data, max_data]) - - if compared_species == 'h': - default_title = '$H^+$ eq. conc. (mol/L)' - elif compared_species == 'z': - default_title = '{0} eq. conc. (mol/L)'.format(extractant_name) - else: - phase = name_breakdown[1] - if phase == 'aq': - re_charge = re_charges[re_species_list.index(compared_species)] - default_title = '$%s^{%d+}$ eq. conc. (mol/L)' \ - % (compared_species, re_charge) - elif phase == 'd': - default_title = '{0} distribution ratio'.format( - compared_species) - else: - default_title = '{0} complex eq. conc. (mol/L)'.format( - compared_species) - fig, ax = plt.subplots() - if isinstance(data_labels, list): - unique_labels = list(set(data_labels)) - for label in unique_labels: - filtered_data = combined_df[combined_df['label'] == label] - filtered_meas = filtered_data['meas'] - filtered_pred = filtered_data['pred'] - ax.scatter(filtered_meas, filtered_pred, label=label) - if legend: - ax.legend(loc='best') - - elif c_data is not None: - if isinstance(c_data, str): - c_data = combined_df[c_data].values - p1 = ax.scatter(meas, pred, c=c_data, alpha=1, cmap='viridis') - c_bar = fig.colorbar(p1, format='%.2f') - if c_label is not None: - c_bar.set_label(c_label, rotation=270, labelpad=20) - else: - sns.scatterplot(meas, pred, color="r", - legend=False) - ax.plot(min_max_data, min_max_data, color="b", label="") - - if print_r_squared: - ax.text(min_max_data[0], - min_max_data[1] * 0.9, - '$R^2$={0:.2f}'.format(self.r_squared(compared_value))) - # plt.legend(loc='lower right') - # else: - # plt.legend() - - ax.set(xlabel='Measured', ylabel='Predicted') - if plot_title is None: - ax.set_title(default_title) - elif isinstance(plot_title, str): - ax.set_title(plot_title) - set_size(8, 6) - plt.tight_layout() - plt.show() - if save_path is not None: - plt.savefig(save_path, bbox_inches='tight') - return fig, ax
- -
[docs] def r_squared(self, compared_value=None): - """r-squared value comparing measured and predicted compared value - - Closer to 1, the better the model's predictions. - - :param compared_value: (str) Quantity to compare predicted and - experimental data. Can be any column containing "eq" in exp_df i.e. - h_eq, z_eq, {RE}_d_eq, etc. default is {RE}_aq_eq - """ - exp_df = self.get_exp_df() - predicted_dict = self.get_predicted_dict() - re_species_list = self._re_species_list - if compared_value is None: - compared_value = '{0}_aq_eq'.format(re_species_list[0]) - pred = pd.DataFrame(predicted_dict)[compared_value].fillna(0).values - predicted_y = np.array(pred) - actual_y = exp_df[compared_value].fillna(0).values - name_breakdown = re.findall('[^_\W]+', compared_value) - compared_species = name_breakdown[0] - if compared_species == 'h': - feed_molarity = exp_df['h_i'].fillna(0).values - elif compared_species == 'z': - feed_molarity = exp_df['z_i'].fillna(0).values - else: - feed_molarity = exp_df[ - '{0}_aq_i'.format(compared_species)].fillna(0).values - combined_df = pd.DataFrame({'pred': predicted_y, - 'meas': actual_y, - 'in_moles': feed_molarity}) - combined_df = combined_df[(combined_df['in_moles'] != 0)] - actual_y = combined_df['meas'].values - predicted_y = combined_df['pred'].values - num = sum((actual_y - predicted_y) ** 2) - den = sum((actual_y - np.mean(actual_y)) ** 2) - if den == 0: - r_2 = 0 - else: - r_2 = (1 - num / den) - return r_2
- -
[docs] @staticmethod - def plot_3d_data(x_data, - y_data, - z_data, - c_data=None, - x_label=None, - y_label=None, - z_label=None, - c_label=None): - """ - - :param x_data: (list) list of data for x axis - :param y_data: (list) list of data for y axis - :param z_data: (list) list of data for z axis - :param c_data: (list) list of data for color axis - :param x_label: (str) label for x axis - :param y_label: (str) label for y axis - :param z_label: (str) label for z axis - :param c_label: (str) label for color axis - :return: - """ - - fig = plt.figure() - ax = fig.add_subplot(111, projection='3d') - if c_data is None: - ax.plot(x_data, y_data, z_data, 'o') - else: - p1 = ax.scatter(x_data, - y_data, - z_data, 'o', c=c_data, - cmap='viridis', alpha=1) - c_bar = fig.colorbar(p1) - if c_label is not None: - c_bar.set_label(c_label, rotation=270, labelpad=20) - if x_label is None: - ax.set_xlabel('x', labelpad=15) - else: - ax.set_xlabel(x_label, labelpad=15) - if y_label is None: - ax.set_ylabel('y', labelpad=15) - else: - ax.set_ylabel(y_label, labelpad=15) - if z_label is None: - ax.set_zlabel('z', labelpad=15) - else: - ax.set_zlabel(z_label, labelpad=15) - plt.show() - return fig, ax
-
- -
- -
-
- - -
- -
-

- © Copyright 2020, Titus Quah, Nwike Iloeje - -

-
- Built with Sphinx using a theme provided by Read the Docs. - -
- -
-
- -
- -
- - - - - - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_sources/guide/about.rst.txt b/docs/_build/html/_sources/guide/about.rst.txt new file mode 100644 index 0000000..dbd1116 --- /dev/null +++ b/docs/_build/html/_sources/guide/about.rst.txt @@ -0,0 +1,61 @@ +.. _about: + +************ +About +************ + +Authors +============= +Titus Quah, University of Utah, + +Nwike Iloeje, Argonne National Laboratory, + +Acknowledgements +================ +Based upon work supported by funding from Argonne National Laboratory provided by the U.S. Department of Energy, Office of Energy Efficiency and Renewable Energy (EERE), under contract DE-AC02-06CH11357 + +References +========== + +If you use LLEPE in your research , we kindly request that you cite the package as follows: + +T. Quah and C. O. Iloeje, “Liquid--Liquid Extraction Thermodynamic Parameter Estimator (LLEPE) for Multicomponent Separation Systems,” in Materials Processing Fundamentals 2021, 2021, pp. 107–120, doi: https://doi.org/10.1007/978-3-030-65253-1_9. + +License +======= + +:: + + Copyright © 2020, UChicago Argonne, LLC + All Rights Reserved + Software Name: LLEPE + By: Argonne National Laboratory, University of Utah + OPEN SOURCE LICENSE + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + + ****************************************************************************************************** + DISCLAIMER + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. + *************************************************************************************************** diff --git a/docs/_build/html/_sources/guide/install.rst.txt b/docs/_build/html/_sources/guide/install.rst.txt index ce1a1f9..e7306d7 100644 --- a/docs/_build/html/_sources/guide/install.rst.txt +++ b/docs/_build/html/_sources/guide/install.rst.txt @@ -24,17 +24,17 @@ To install the latest master version: .. code-block:: bash - pip install git+https://xgitlab.cels.anl.gov/summer-2020/parameter-estimation.git + pip install git+https://github.com/ANL-CEEESA/LLEPE.git Development version =================== -To contribute to Stable-Baselines, with support for running tests and building the documentation. +To contribute to LLEPE, with support for running tests and building the documentation. .. code-block:: bash - git clone https://xgitlab.cels.anl.gov/summer-2020/parameter-estimation.git && cd parameter-estimation + git clone https://github.com/ANL-CEEESA/LLEPE.git pip install -e .[docs,tests] diff --git a/docs/_build/html/_sources/index.rst.txt b/docs/_build/html/_sources/index.rst.txt index 9a973a9..a0c905b 100644 --- a/docs/_build/html/_sources/index.rst.txt +++ b/docs/_build/html/_sources/index.rst.txt @@ -1,9 +1,8 @@ -.. reeps documentation master file, created by Titus Quah - sphinx-quickstart on Tue Jun 9 10:13:23 2020. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +.. LLEPE: Liquid-Liquid Equilibrium Parameter Estimator + Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. + Released under the modified BSD license. See LICENSE for more details. -Welcome to llepe's docs! - the Liquid-Liquid Extraction Parameter Estimator +Welcome to LLEPE's docs! - the Liquid-Liquid Extraction Parameter Estimator =========================================================================== LLEPE is a package for thermodynamic parameter estimation for liquid-liquid extraction modeling @@ -19,12 +18,13 @@ Error between predicted and experimental data is then minimized. guide/install guide/quickstart + guide/about .. toctree:: :maxdepth: 1 :caption: Searchers - modules/reeps + modules/LLEPE diff --git a/docs/_source/modules/reeps.rst b/docs/_build/html/_sources/modules/LLEPE.rst.txt similarity index 74% rename from docs/_source/modules/reeps.rst rename to docs/_build/html/_sources/modules/LLEPE.rst.txt index bba5c13..41daa9d 100644 --- a/docs/_source/modules/reeps.rst +++ b/docs/_build/html/_sources/modules/LLEPE.rst.txt @@ -1,4 +1,4 @@ -.. _reeps: +.. _LLEPE: .. automodule:: llepe diff --git a/docs/_build/html/_sources/modules/reeps.rst.txt b/docs/_build/html/_sources/modules/reeps.rst.txt index bba5c13..41daa9d 100644 --- a/docs/_build/html/_sources/modules/reeps.rst.txt +++ b/docs/_build/html/_sources/modules/reeps.rst.txt @@ -1,4 +1,4 @@ -.. _reeps: +.. _LLEPE: .. automodule:: llepe diff --git a/docs/_build/html/_static/alabaster.css b/docs/_build/html/_static/alabaster.css deleted file mode 100644 index 0eddaeb..0000000 --- a/docs/_build/html/_static/alabaster.css +++ /dev/null @@ -1,701 +0,0 @@ -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: Georgia, serif; - font-size: 17px; - background-color: #fff; - color: #000; - margin: 0; - padding: 0; -} - - -div.document { - width: 940px; - margin: 30px auto 0 auto; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 220px; -} - -div.sphinxsidebar { - width: 220px; - font-size: 14px; - line-height: 1.5; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #fff; - color: #3E4349; - padding: 0 30px 0 30px; -} - -div.body > .section { - text-align: left; -} - -div.footer { - width: 940px; - margin: 20px auto 30px auto; - font-size: 14px; - color: #888; - text-align: right; -} - -div.footer a { - color: #888; -} - -p.caption { - font-family: inherit; - font-size: inherit; -} - - -div.relations { - display: none; -} - - -div.sphinxsidebar a { - color: #444; - text-decoration: none; - border-bottom: 1px dotted #999; -} - -div.sphinxsidebar a:hover { - border-bottom: 1px solid #999; -} - -div.sphinxsidebarwrapper { - padding: 18px 10px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 0; - margin: -10px 0 0 0px; - text-align: center; -} - -div.sphinxsidebarwrapper h1.logo { - margin-top: -10px; - text-align: center; - margin-bottom: 5px; - text-align: left; -} - -div.sphinxsidebarwrapper h1.logo-name { - margin-top: 0px; -} - -div.sphinxsidebarwrapper p.blurb { - margin-top: 0; - font-style: normal; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: Georgia, serif; - color: #444; - font-size: 24px; - font-weight: normal; - margin: 0 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 20px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p.logo a, -div.sphinxsidebar h3 a, -div.sphinxsidebar p.logo a:hover, -div.sphinxsidebar h3 a:hover { - border: none; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar ul li.toctree-l1 > a { - font-size: 120%; -} - -div.sphinxsidebar ul li.toctree-l2 > a { - font-size: 110%; -} - -div.sphinxsidebar input { - border: 1px solid #CCC; - font-family: Georgia, serif; - font-size: 1em; -} - -div.sphinxsidebar hr { - border: none; - height: 1px; - color: #AAA; - background: #AAA; - - text-align: left; - margin-left: 0; - width: 50%; -} - -div.sphinxsidebar .badge { - border-bottom: none; -} - -div.sphinxsidebar .badge:hover { - border-bottom: none; -} - -/* To address an issue with donation coming after search */ -div.sphinxsidebar h3.donation { - margin-top: 10px; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: Georgia, serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #DDD; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #EAEAEA; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - margin: 20px 0px; - padding: 10px 30px; - background-color: #EEE; - border: 1px solid #CCC; -} - -div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fafafa; -} - -div.admonition p.admonition-title { - font-family: Georgia, serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight { - background-color: #fff; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.warning { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.danger { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.error { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.caution { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.attention { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.important { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.note { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.tip { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.hint { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.seealso { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.topic { - background-color: #EEE; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt, code { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.9em; -} - -.hll { - background-color: #FFC; - margin: 0 -12px; - padding: 0 12px; - display: block; -} - -img.screenshot { -} - -tt.descname, tt.descclassname, code.descname, code.descclassname { - font-size: 0.95em; -} - -tt.descname, code.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #EEE; - background: #FDFDFD; - font-size: 0.9em; -} - -table.footnote + table.footnote { - margin-top: -15px; - border-top: none; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.field-list p { - margin-bottom: 0.8em; -} - -/* Cloned from - * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 - */ -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -table.footnote td.label { - width: .1px; - padding: 0.3em 0 0.3em 0.5em; -} - -table.footnote td { - padding: 0.3em 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -blockquote { - margin: 0 0 0 30px; - padding: 0; -} - -ul, ol { - /* Matches the 30px from the narrow-screen "li > ul" selector below */ - margin: 10px 0 10px 30px; - padding: 0; -} - -pre { - background: #EEE; - padding: 7px 30px; - margin: 15px 0px; - line-height: 1.3em; -} - -div.viewcode-block:target { - background: #ffd; -} - -dl pre, blockquote pre, li pre { - margin-left: 0; - padding-left: 30px; -} - -tt, code { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, code.xref, a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fff; -} - -a.reference { - text-decoration: none; - border-bottom: 1px dotted #004B6B; -} - -/* Don't put an underline on images */ -a.image-reference, a.image-reference:hover { - border-bottom: none; -} - -a.reference:hover { - border-bottom: 1px solid #6D4100; -} - -a.footnote-reference { - text-decoration: none; - font-size: 0.7em; - vertical-align: top; - border-bottom: 1px dotted #004B6B; -} - -a.footnote-reference:hover { - border-bottom: 1px solid #6D4100; -} - -a:hover tt, a:hover code { - background: #EEE; -} - - -@media screen and (max-width: 870px) { - - div.sphinxsidebar { - display: none; - } - - div.document { - width: 100%; - - } - - div.documentwrapper { - margin-left: 0; - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - } - - div.bodywrapper { - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; - } - - ul { - margin-left: 0; - } - - li > ul { - /* Matches the 30px from the "ul, ol" selector above */ - margin-left: 30px; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .bodywrapper { - margin: 0; - } - - .footer { - width: auto; - } - - .github { - display: none; - } - - - -} - - - -@media screen and (max-width: 875px) { - - body { - margin: 0; - padding: 20px 30px; - } - - div.documentwrapper { - float: none; - background: #fff; - } - - div.sphinxsidebar { - display: block; - float: none; - width: 102.5%; - margin: 50px -30px -20px -30px; - padding: 10px 20px; - background: #333; - color: #FFF; - } - - div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, - div.sphinxsidebar h3 a { - color: #fff; - } - - div.sphinxsidebar a { - color: #AAA; - } - - div.sphinxsidebar p.logo { - display: none; - } - - div.document { - width: 100%; - margin: 0; - } - - div.footer { - display: none; - } - - div.bodywrapper { - margin: 0; - } - - div.body { - min-height: 0; - padding: 0; - } - - .rtd_doc_footer { - display: none; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .footer { - width: auto; - } - - .github { - display: none; - } -} - - -/* misc. */ - -.revsys-inline { - display: none!important; -} - -/* Make nested-list/multi-paragraph items look better in Releases changelog - * pages. Without this, docutils' magical list fuckery causes inconsistent - * formatting between different release sub-lists. - */ -div#changelog > div.section > ul > li > p:only-child { - margin-bottom: 0; -} - -/* Hide fugly table cell borders in ..bibliography:: directive output */ -table.docutils.citation, table.docutils.citation td, table.docutils.citation th { - border: none; - /* Below needed in some edge cases; if not applied, bottom shadows appear */ - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - - -/* relbar */ - -.related { - line-height: 30px; - width: 100%; - font-size: 0.9rem; -} - -.related.top { - border-bottom: 1px solid #EEE; - margin-bottom: 20px; -} - -.related.bottom { - border-top: 1px solid #EEE; -} - -.related ul { - padding: 0; - margin: 0; - list-style: none; -} - -.related li { - display: inline; -} - -nav#rellinks { - float: right; -} - -nav#rellinks li+li:before { - content: "|"; -} - -nav#breadcrumbs li+li:before { - content: "\00BB"; -} - -/* Hide certain items when printing */ -@media print { - div.related { - display: none; - } -} \ No newline at end of file diff --git a/docs/_build/html/_static/classic.css b/docs/_build/html/_static/classic.css deleted file mode 100644 index b4fd80f..0000000 --- a/docs/_build/html/_static/classic.css +++ /dev/null @@ -1,266 +0,0 @@ -/* - * classic.css_t - * ~~~~~~~~~~~~~ - * - * Sphinx stylesheet -- classic theme. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -html { - /* CSS hack for macOS's scrollbar (see #1125) */ - background-color: #FFFFFF; -} - -body { - font-family: sans-serif; - font-size: 100%; - background-color: #11303d; - color: #000; - margin: 0; - padding: 0; -} - -div.document { - background-color: #1c4e63; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 230px; -} - -div.body { - background-color: #ffffff; - color: #000000; - padding: 0 20px 30px 20px; -} - -div.footer { - color: #ffffff; - width: 100%; - padding: 9px 0 9px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #ffffff; - text-decoration: underline; -} - -div.related { - background-color: #133f52; - line-height: 30px; - color: #ffffff; -} - -div.related a { - color: #ffffff; -} - -div.sphinxsidebar { -} - -div.sphinxsidebar h3 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.4em; - font-weight: normal; - margin: 0; - padding: 0; -} - -div.sphinxsidebar h3 a { - color: #ffffff; -} - -div.sphinxsidebar h4 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.3em; - font-weight: normal; - margin: 5px 0 0 0; - padding: 0; -} - -div.sphinxsidebar p { - color: #ffffff; -} - -div.sphinxsidebar p.topless { - margin: 5px 10px 10px 10px; -} - -div.sphinxsidebar ul { - margin: 10px; - padding: 0; - color: #ffffff; -} - -div.sphinxsidebar a { - color: #98dbcc; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - - - -/* -- hyperlink styles ------------------------------------------------------ */ - -a { - color: #355f7c; - text-decoration: none; -} - -a:visited { - color: #355f7c; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - - - -/* -- body styles ----------------------------------------------------------- */ - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Trebuchet MS', sans-serif; - background-color: #f2f2f2; - font-weight: normal; - color: #20435c; - border-bottom: 1px solid #ccc; - margin: 20px -20px 10px -20px; - padding: 3px 0 3px 10px; -} - -div.body h1 { margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 160%; } -div.body h3 { font-size: 140%; } -div.body h4 { font-size: 120%; } -div.body h5 { font-size: 110%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - text-align: justify; - line-height: 130%; -} - -div.admonition p.admonition-title + p { - display: inline; -} - -div.admonition p { - margin-bottom: 5px; -} - -div.admonition pre { - margin-bottom: 5px; -} - -div.admonition ul, div.admonition ol { - margin-bottom: 5px; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre { - padding: 5px; - background-color: #eeffcc; - color: #333333; - line-height: 120%; - border: 1px solid #ac9; - border-left: none; - border-right: none; -} - -code { - background-color: #ecf0f3; - padding: 0 1px 0 1px; - font-size: 0.95em; -} - -th, dl.field-list > dt { - background-color: #ede; -} - -.warning code { - background: #efc2c2; -} - -.note code { - background: #d6d6d6; -} - -.viewcode-back { - font-family: sans-serif; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #ac9; - border-bottom: 1px solid #ac9; -} - -div.code-block-caption { - color: #efefef; - background-color: #1c4e63; -} \ No newline at end of file diff --git a/docs/_build/html/_static/custom.css b/docs/_build/html/_static/custom.css deleted file mode 100644 index 2a924f1..0000000 --- a/docs/_build/html/_static/custom.css +++ /dev/null @@ -1 +0,0 @@ -/* This file intentionally left blank. */ diff --git a/docs/_build/html/_static/default.css b/docs/_build/html/_static/default.css deleted file mode 100644 index 81b9363..0000000 --- a/docs/_build/html/_static/default.css +++ /dev/null @@ -1 +0,0 @@ -@import url("classic.css"); diff --git a/docs/_build/html/_static/sidebar.js b/docs/_build/html/_static/sidebar.js deleted file mode 100644 index 657f8be..0000000 --- a/docs/_build/html/_static/sidebar.js +++ /dev/null @@ -1,159 +0,0 @@ -/* - * sidebar.js - * ~~~~~~~~~~ - * - * This script makes the Sphinx sidebar collapsible. - * - * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds - * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton - * used to collapse and expand the sidebar. - * - * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden - * and the width of the sidebar and the margin-left of the document - * are decreased. When the sidebar is expanded the opposite happens. - * This script saves a per-browser/per-session cookie used to - * remember the position of the sidebar among the pages. - * Once the browser is closed the cookie is deleted and the position - * reset to the default (expanded). - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -$(function() { - - - - - - - - - // global elements used by the functions. - // the 'sidebarbutton' element is defined as global after its - // creation, in the add_sidebar_button function - var bodywrapper = $('.bodywrapper'); - var sidebar = $('.sphinxsidebar'); - var sidebarwrapper = $('.sphinxsidebarwrapper'); - - // for some reason, the document has no sidebar; do not run into errors - if (!sidebar.length) return; - - // original margin-left of the bodywrapper and width of the sidebar - // with the sidebar expanded - var bw_margin_expanded = bodywrapper.css('margin-left'); - var ssb_width_expanded = sidebar.width(); - - // margin-left of the bodywrapper and width of the sidebar - // with the sidebar collapsed - var bw_margin_collapsed = '.8em'; - var ssb_width_collapsed = '.8em'; - - // colors used by the current theme - var dark_color = $('.related').css('background-color'); - var light_color = $('.document').css('background-color'); - - function sidebar_is_collapsed() { - return sidebarwrapper.is(':not(:visible)'); - } - - function toggle_sidebar() { - if (sidebar_is_collapsed()) - expand_sidebar(); - else - collapse_sidebar(); - } - - function collapse_sidebar() { - sidebarwrapper.hide(); - sidebar.css('width', ssb_width_collapsed); - bodywrapper.css('margin-left', bw_margin_collapsed); - sidebarbutton.css({ - 'margin-left': '0', - 'height': bodywrapper.height() - }); - sidebarbutton.find('span').text('»'); - sidebarbutton.attr('title', _('Expand sidebar')); - document.cookie = 'sidebar=collapsed'; - } - - function expand_sidebar() { - bodywrapper.css('margin-left', bw_margin_expanded); - sidebar.css('width', ssb_width_expanded); - sidebarwrapper.show(); - sidebarbutton.css({ - 'margin-left': ssb_width_expanded-12, - 'height': bodywrapper.height() - }); - sidebarbutton.find('span').text('«'); - sidebarbutton.attr('title', _('Collapse sidebar')); - document.cookie = 'sidebar=expanded'; - } - - function add_sidebar_button() { - sidebarwrapper.css({ - 'float': 'left', - 'margin-right': '0', - 'width': ssb_width_expanded - 28 - }); - // create the button - sidebar.append( - '
«
' - ); - var sidebarbutton = $('#sidebarbutton'); - light_color = sidebarbutton.css('background-color'); - // find the height of the viewport to center the '<<' in the page - var viewport_height; - if (window.innerHeight) - viewport_height = window.innerHeight; - else - viewport_height = $(window).height(); - sidebarbutton.find('span').css({ - 'display': 'block', - 'margin-top': (viewport_height - sidebar.position().top - 20) / 2 - }); - - sidebarbutton.click(toggle_sidebar); - sidebarbutton.attr('title', _('Collapse sidebar')); - sidebarbutton.css({ - 'color': '#FFFFFF', - 'border-left': '1px solid ' + dark_color, - 'font-size': '1.2em', - 'cursor': 'pointer', - 'height': bodywrapper.height(), - 'padding-top': '1px', - 'margin-left': ssb_width_expanded - 12 - }); - - sidebarbutton.hover( - function () { - $(this).css('background-color', dark_color); - }, - function () { - $(this).css('background-color', light_color); - } - ); - } - - function set_position_from_cookie() { - if (!document.cookie) - return; - var items = document.cookie.split(';'); - for(var k=0; k
  • Installation
  • Getting Started
  • +
  • About
  • Searchers

    @@ -169,7 +170,7 @@

    F

    @@ -177,45 +178,45 @@

    G

    @@ -227,14 +228,14 @@ llepe @@ -246,7 +247,7 @@ module @@ -255,11 +256,11 @@

    P

    @@ -267,7 +268,7 @@

    R

    @@ -275,45 +276,45 @@

    S

    @@ -321,13 +322,13 @@

    U

    @@ -344,7 +345,7 @@

    - © Copyright 2020, Titus Quah, Nwike Iloeje + © Copyright 2020, UChicago Argonne, LLC.

    diff --git a/docs/_build/html/guide/about.html b/docs/_build/html/guide/about.html new file mode 100644 index 0000000..67be0bd --- /dev/null +++ b/docs/_build/html/guide/about.html @@ -0,0 +1,272 @@ + + + + + + + + + + + About — LLEPE 1.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
    +
    + +
    +

    About

    +
    +

    Authors

    +

    Titus Quah, University of Utah, <titus{dot}quah{at}gmail{dot}com>

    +

    Nwike Iloeje, Argonne National Laboratory, <ciloeje{at}anl{dot}gov>

    +
    +
    +

    Acknowledgements

    +

    Based upon work supported by funding from Argonne National Laboratory provided by the U.S. Department of Energy, Office of Energy Efficiency and Renewable Energy (EERE), under contract DE-AC02-06CH11357

    +
    +
    +

    References

    +

    If you use LLEPE in your research , we kindly request that you cite the package as follows:

    +
      +
    1. Quah and C. O. Iloeje, “Liquid--Liquid Extraction Thermodynamic Parameter Estimator (LLEPE) for Multicomponent Separation Systems,” in Materials Processing Fundamentals 2021, 2021, pp. 107–120, doi: https://doi.org/10.1007/978-3-030-65253-1_9.

    2. +
    +
    +
    +

    License

    +
    Copyright © 2020, UChicago Argonne, LLC
    +All Rights Reserved
    + Software Name: LLEPE
    +By: Argonne National Laboratory, University of Utah
    +OPEN SOURCE LICENSE
    +
    +Redistribution and use in source and binary forms, with or without
    +modification, are permitted provided that the following conditions are met:
    +
    +1. Redistributions of source code must retain the above copyright notice,
    +   this list of conditions and the following disclaimer.
    +2. Redistributions in binary form must reproduce the above copyright notice,
    +   this list of conditions and the following disclaimer in the documentation
    +   and/or other materials provided with the distribution.
    +3. Neither the name of the copyright holder nor the names of its contributors
    +   may be used to endorse or promote products derived from this software without
    +   specific prior written permission.
    +
    +
    +******************************************************************************************************
    +DISCLAIMER
    +
    +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
    +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
    +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
    +OF SUCH DAMAGE.
    +***************************************************************************************************
    +
    +
    +
    +
    + + +
    + +
    + + +
    +
    + +
    + +
    + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/_build/html/guide/install.html b/docs/_build/html/guide/install.html index f28dddc..17e566e 100644 --- a/docs/_build/html/guide/install.html +++ b/docs/_build/html/guide/install.html @@ -36,7 +36,7 @@ - + @@ -97,7 +97,7 @@

    Searchers

    @@ -175,14 +175,14 @@

    Bleeding-edge version

    To install the latest master version:

    -
    pip install git+https://xgitlab.cels.anl.gov/summer-2020/parameter-estimation.git
    +
    pip install git+https://github.com/ANL-CEEESA/LLEPE.git
     

    Development version

    -

    To contribute to Stable-Baselines, with support for running tests and building the documentation.

    -
    git clone https://xgitlab.cels.anl.gov/summer-2020/parameter-estimation.git && cd parameter-estimation
    +

    To contribute to LLEPE, with support for running tests and building the documentation.

    +
    git clone https://github.com/ANL-CEEESA/LLEPE.git
     pip install -e .[docs,tests]
     
    @@ -204,7 +204,7 @@ pip install -e .[docs,tests] - +
    @@ -213,7 +213,7 @@ pip install -e .[docs,tests]

    - © Copyright 2020, Titus Quah, Nwike Iloeje + © Copyright 2020, UChicago Argonne, LLC.

    diff --git a/docs/_build/html/guide/quickstart.html b/docs/_build/html/guide/quickstart.html index 948381e..72b8974 100644 --- a/docs/_build/html/guide/quickstart.html +++ b/docs/_build/html/guide/quickstart.html @@ -218,7 +218,7 @@ aqueous phase.

    - © Copyright 2020, Titus Quah, Nwike Iloeje + © Copyright 2020, UChicago Argonne, LLC.

    diff --git a/docs/_build/html/index.html b/docs/_build/html/index.html index 7f6af8e..9cee7a4 100644 --- a/docs/_build/html/index.html +++ b/docs/_build/html/index.html @@ -8,7 +8,7 @@ - Welcome to llepe's docs! - the Liquid-Liquid Extraction Parameter Estimator — LLEPE 1.0.0 documentation + Welcome to LLEPE's docs! - the Liquid-Liquid Extraction Parameter Estimator — LLEPE 1.0.0 documentation @@ -86,10 +86,11 @@

    Searchers

    @@ -135,7 +136,7 @@
  • Docs »
  • -
  • Welcome to llepe's docs! - the Liquid-Liquid Extraction Parameter Estimator
  • +
  • Welcome to LLEPE's docs! - the Liquid-Liquid Extraction Parameter Estimator
  • @@ -155,7 +156,7 @@
    -

    Welcome to llepe's docs! - the Liquid-Liquid Extraction Parameter Estimator

    +

    Welcome to LLEPE's docs! - the Liquid-Liquid Extraction Parameter Estimator

    LLEPE is a package for thermodynamic parameter estimation for liquid-liquid extraction modeling

    LLEPE takes experimental data in a csv and system data in a xml.

    The package then uses Cantera, another python package, to simulate equilibrium.

    @@ -172,12 +173,19 @@
  • Getting Started
  • +
  • About +
  • Searchers

    @@ -208,7 +216,7 @@

    - © Copyright 2020, Titus Quah, Nwike Iloeje + © Copyright 2020, UChicago Argonne, LLC.

    diff --git a/docs/_build/html/modules/LLEPE.html b/docs/_build/html/modules/LLEPE.html new file mode 100644 index 0000000..4f1ff3b --- /dev/null +++ b/docs/_build/html/modules/LLEPE.html @@ -0,0 +1,1123 @@ + + + + + + + + + + + LLEPE — LLEPE 1.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
    +
    + +
    +

    LLEPE

    +
    +

    Parameters

    +
    +
    +class llepe.LLEPE(exp_data, phases_xml_filename, phase_names, aq_solvent_name, extractant_name, diluant_name, complex_names, extracted_species_ion_names, extracted_species_list=None, aq_solvent_rho=None, extractant_rho=None, diluant_rho=None, opt_dict=None, objective_function='Log-MSE', optimizer='scipy_minimize', temp_xml_file_path=None, dependant_params_dict=None, custom_objects_dict=None)[source]
    +

    Liquid-Liquid Extraction Parameter estimator

    +
    +

    Note

    +

    The order in which the extracted species (ES) appear in the csv file +must be the same order as they appear in the xml, complex_names and +extracted_species_ion_names.

    +

    For example, say in exp_data, ES_1 is Nd ES_2 is Pr, +and

    +
    aq_solvent_name = 'H2O(L)'
    +extractant_name = '(HA)2(org)'
    +diluent_name = 'dodecane'
    +
    +
    +

    Then:

    +

    The exp_data column ordering must be (names do not matter):

    +

    [h_i, h_eq, z_i, z_eq, Nd_aq_i, Nd_aq_eq, Nd_d_eq, +Pr_aq_i, Pr_aq_eq, Pr_d_eq]

    +

    The aqueous speciesArray must be +"H2O(L) H+ OH- Cl- Nd+++ Pr+++"

    +

    The organic speciesArray must be +"(HA)2(org) dodecane Nd(H(A)2)3(org) Pr(H(A)2)3(org)"

    +
    complex_names = ['Nd(H(A)2)3(org)', 'Pr(H(A)2)3(org)']
    +extracted_species_ion_names = ['Nd+++', 'Pr+++']
    +
    +
    +
    +
    +
    Parameters
    +
      +
    • exp_data --

      (str or pd.DataFrame) csv file name +or DataFrame with experimental data

      +

      In the .csv file, the rows are different experiments and +columns are the measured quantities.

      +

      The ordering of the columns needs to be:

      +

      [h_i, h_eq, z_i, z_eq, +{ES_1}_aq_i, {ES_1}_aq_eq, {ES_1}_d_eq, +{ES_2}_aq_i, {ES_2}_aq_eq, {ES_2}_d_eq,... +{ES_N}_aq_i, {ES_N}_aq_eq, {ES_N}_d_eq]

      +

      Naming does not matter, just the order.

      +

      Where {ES_1}-{ES_N} are the extracted species names of interest +i.e. Nd, Pr, La, etc.

      +

      Below is an explanation of the columns.

      + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Index

      Column

      Meaning

      0

      h_i

      Initial Concentration of +H+ ions (mol/L)

      1

      h_eq

      Equilibrium concentration of +H+ ions (mol/L)

      2

      z_i

      Initial concentration of +extractant (mol/L)

      3

      z_eq

      Equilibrium concentration of +extractant (mol/L)

      4

      {ES}_aq_i

      Initial concentration of ES ions (mol/L)

      5

      {ES}_aq_eq

      Equilibrium concentration of ES ions +in aqueous phase (mol/L)

      6

      {ES}_d_eq

      Equilibrium Ratio between amount of +ES atoms in organic to aqueous

      +

    • +
    • phases_xml_filename --

      (str) xml file with parameters +for equilibrium calc

      +

      Would recommend copying and modifying xmls located in data/xmls +or in Cantera's "data" folder

      +

      speciesArray fields need specific ordering.

      +

      In aqueous phase: aq_solvent_name, H+, OH-, Cl-, ES_1, ES_2, ..., ES_N

      +

      (ES_1-ES_N) represent ES ion names i.e. Nd+++, Pr+++

      +

      In organic phase : extractant_name, diluant_name, ES_1, ES_2, ..., ES_N

      +

      (ES_1-ES_N) represent ES complex names +i.e. Nd(H(A)2)3(org), Pr(H(A)2)3(org)

      +

    • +
    • phase_names --

      (list) names of phases in xml file

      +

      Found in the xml file under <phase ... id={phase_name}>

      +

    • +
    • aq_solvent_name -- (str) name of aqueous solvent in xml file

    • +
    • extractant_name -- (str) name of extractant in xml file

    • +
    • diluant_name -- (str) name of diluant in xml file

    • +
    • complex_names -- (list) names of complexes in xml file.

    • +
    • extracted_species_ion_names -- (list) names of extracted species ions +in xml file

    • +
    • extracted_species_list --

      (list) names of extracted species elements.

      +

      If None, extracted_species_list will be extracted_species_ion_names +without '+' i.e. 'Nd+++'->'Nd'

      +

    • +
    • aq_solvent_rho --

      (float) density of solvent (g/L)

      +

      If None, molar volume/molecular weight is used from xml

      +

    • +
    • extractant_rho --

      (float) density of extractant (g/L)

      +

      If None, molar volume/molecular weight is used from xml

      +

    • +
    • diluant_rho --

      (float) density of diluant (g/L)

      +

      If None, molar volume/molecular weight is used from xml

      +

    • +
    • opt_dict --

      (dict) dictionary containing info about which +species parameters are updated to fit model to experimental data

      +

      Should have the format as below. Dictionary keys under user defined +parameter name must be named as shown below ('upper_element_name', +'upper_attrib_name', etc.). 'attrib_name's and 'attrib_value's can +be None. {} denotes areas for user to fill in.

      +
      opt_dict = {"{user_defined_name_for_parameter_1}":
      +                {'upper_element_name': {param_upper_element},
      +                'upper_attrib_name': {param_upper_attrib_name},
      +                'upper_attrib_value': {param_upper_attrib_value},
      +                'lower_element_name': {param_lower_element},
      +                'lower_attrib_name': {param_lower_attrib_name},
      +                'lower_attrib_value': {param_lower_attrib_value},
      +                'input_format': {str format to input input_value}
      +                'input_value': {guess_value}},
      +            "{user_defined_name_for_parameter_2}":
      +                            ...
      +            ...
      +            }
      +
      +
      +

      See example files for more examples.

      +

    • +
    • objective_function --

      (function or str) function to compute objective

      +

      By default, the objective function is log mean squared error +of distribution ratio

      +
      np.sum((np.log10(d_pred)-np.log10(d_meas))^2)
      +
      +
      +

      Function needs to take inputs:

      +
      objective_function(predicted_dict, measured_df, kwargs)
      +
      +
      +

      kwargs is optional

      +

      Function needs to return: (float) value computed by objective function

      +

      Below is the guide for referencing predicted values

      + ++++ + + + + + + + + + + + + + + + + + + + + + + +

      To access

      Use

      hydrogen ion conc in aq

      predicted_dict['h_eq']

      extractant conc in org

      predicted_dict['z_eq']

      ES ion eq conc in aq

      predicted_dict['{ES}_aq_eq']

      ES complex eq conc in org

      predicted_dict['{ES}_org_eq']

      ES distribution ratio

      predicted_dict['{ES}_d_eq']

      +

      Replace "{ES}" with extracted species element i.e. Nd, La, etc.

      +

      For measured values, use the same names, but +replace predicted_dict with measured_df

      +

    • +
    • optimizer --

      (function or str) function to perform optimization

      +
      +

      Note

      +

      The optimized variables are not directly the species parameters, +but instead are first multiplied by the initial guess before +sending becoming the species parameters.

      +

      For example, say

      +
      opt_dict = {'Nd(H(A)2)3(org):'h0':-4.7e6}
      +
      +
      +

      If the bounds on h0 need to be [-4.7e7,-4.7e5], then +divide the bounds by the guess and get

      +
      "bounds": [(1e-1, 1e1)]
      +
      +
      +
      +

      By default, the optimizer is scipy's optimize function with

      +
      default_kwargs= {"method": 'SLSQP',
      +                 "bounds": [(1e-1, 1e1)] * len(x_guess),
      +                 "constraints": (),
      +                 "options": {'disp': True,
      +                             'maxiter': 1000,
      +                             'ftol': 1e-6}}
      +
      +
      +

      Function needs to take inputs: +optimizer(objective_function, x_guess, kwargs)

      +

      kwargs is optional

      +
      +
      Function needs to return: ((np.ndarray, float)) Optimized parameters,

      objective_function value

      +
      +
      +

    • +
    • temp_xml_file_path --

      (str) path to temporary xml file.

      +

      This xml file is a duplicate of the phases_xml_file name and is +modified during the optimization process to avoid changing the original +xml file

      +

      default is local temp folder

      +

    • +
    • dependant_params_dict --

      (dict) dictionary containing information +about parameters dependant on opt_dict. Has a similar structure to +opt_dict except instead of input values, it has 3 other fields: +'function', 'kwargs', and 'independent_params.

      +

      'function' is a function of the form

      +

      function(independent_param__value_list, custom_objects_dict, +**kwargs)

      +

      'kwargs' are the extra arguments to pass to function

      +

      'independent_params' is a list of parameter names in opt_dict that the +dependent_param is a function of.

      +

      'custom_objects_dict' is for accessing the estimator's internal +custom_objects_dict and must be included in the arguments, even if the +custom_objects_dict is not set and is None.

      +

      See example code for usage.

      +

    • +
    • custom_objects_dict -- (dict) dictionary containing custom objects +format: {<object_name_string>: <object>,...}

    • +
    +
    +
    +
    +
    +fit(objective_function=None, optimizer=None, objective_kwargs=None, optimizer_kwargs=None) → tuple[source]
    +

    Fits experimental to modeled data by minimizing objective function +with optimizer. Returns dictionary with opt_dict structure

    +
    +
    Parameters
    +
      +
    • objective_function -- (function) function to compute objective +If 'None', last set objective or default function is used

    • +
    • optimizer -- (function) function to perform optimization +If 'None', last set optimizer or default is used

    • +
    • optimizer_kwargs -- (dict) optional arguments for optimizer

    • +
    • objective_kwargs -- (dict) optional arguments +for objective function

    • +
    +
    +
    Returns tuple
    +

    (opt_dict (dict), opt_value (float)) +optimized opt_dict: Has identical structure as opt_dict

    +
    +
    +
    + +
    +
    +get_aq_solvent_name() → str[source]
    +

    Returns aq_solvent_name

    +
    +
    Returns
    +

    aq_solvent_name: (str) name of aqueous solvent in xml file

    +
    +
    +
    + +
    +
    +get_aq_solvent_rho() → str[source]
    +

    Returns aqueous solvent density (g/L)

    +
    +
    Returns
    +

    aq_solvent_rho: (float) density of aqueous solvent

    +
    +
    +
    + +
    +
    +get_complex_names() → list[source]
    +

    Returns list of complex names

    +
    +
    Returns
    +

    complex_names: (list) names of complexes in xml file.

    +
    +
    +
    + +
    +
    +get_custom_objects_dict()[source]
    +

    Returns the custom_objects_dict

    +
    +
    Returns
    +

    custom_objects_dict: (dict) dictionary containing +information about custom objects from user

    +
    +
    +
    + +
    +
    +get_dependant_params_dict()[source]
    +

    Returns the dependant_params_dict

    +
    +
    Returns
    +

    dependant_params_dict: (dict) dictionary containing +information about parameters dependant on opt_dict

    +
    +
    +
    + +
    +
    +get_diluant_name() → str[source]
    +

    Returns diluant name +:return: diluant_name: (str) name of diluant in xml file

    +
    + +
    +
    +get_diluant_rho() → str[source]
    +

    Returns diluant density (g/L)

    +
    +
    Returns
    +

    diluant_rho: (float) density of diluant

    +
    +
    +
    + +
    +
    +get_exp_df() → pandas.core.frame.DataFrame[source]
    +

    Returns the experimental DataFrame

    +
    +
    Returns
    +

    (pd.DataFrame) Experimental data

    +
    +
    +
    + +
    +
    +get_extractant_name() → str[source]
    +

    Returns extractant name

    +
    +
    Returns
    +

    extractant_name: (str) name of extractant in xml file

    +
    +
    +
    + +
    +
    +get_extractant_rho() → str[source]
    +

    Returns extractant density (g/L)

    +
    +
    Returns
    +

    extractant_rho: (float) density of extractant

    +
    +
    +
    + +
    +
    +get_extracted_species_ion_names() → list[source]
    +

    Returns list of extracted species ion names

    +
    +
    Returns
    +

    extracted_species_ion_names: (list) names of +extracted species ions in xml file

    +
    +
    +
    + +
    +
    +get_extracted_species_list() → list[source]
    +

    Returns list of extracted species names

    +
    +
    Returns
    +

    extracted_species_list: (list) names of extracted species in +xml file

    +
    +
    +
    + +
    +
    +get_in_moles() → pandas.core.frame.DataFrame[source]
    +

    Returns the in_moles DataFrame which contains the initial mole +fractions of each species for each experiment

    +
    +
    Returns
    +

    in_moles: (pd.DataFrame) DataFrame with initial mole fractions

    +
    +
    +
    + +
    +
    +get_objective_function()[source]
    +

    Returns objective function

    +
    +
    Returns
    +

    objective_function: (func) Objective function to quantify +error between model and experimental data

    +
    +
    +
    + +
    +
    +get_opt_dict() → dict[source]
    +

    Returns the dictionary containing optimization information

    +
    +
    Returns
    +

    (dict) dictionary containing info about which +species parameters are updated to fit model to experimental data

    +
    +
    +
    + +
    +
    +get_optimizer()[source]
    +

    Returns objective function

    +
    +
    Returns
    +

    optimizer: (func) Optimizer function to minimize objective +function

    +
    +
    +
    + +
    +
    +get_phases() → list[source]
    +

    Returns the list of Cantera solutions

    +
    +
    Returns
    +

    (list) list of Cantera solutions/phases

    +
    +
    +
    + +
    +
    +get_predicted_dict()[source]
    +

    Returns predicted dictionary of species concentrations +that xml parameters predicts given current in_moles

    +
    +
    Returns
    +

    predicted_dict: (dict) dictionary of species concentrations

    +
    +
    +
    + +
    +
    +get_temp_xml_file_path()[source]
    +

    Returns path to temporary xml file.

    +

    This xml file is a duplicate of the phases_xml_file name and is +modified during the optimization process to avoid changing the original +xml file.

    +
    +
    Returns
    +

    temp_xml_file_path: (str) path to temporary xml file.

    +
    +
    +
    + +
    +
    +log_mean_squared_error(predicted_dict, meas_df)[source]
    +

    Default objective function for LLEPE

    +

    Returns the log mean squared error of +predicted distribution ratios (d=n_org/n_aq) +to measured d.

    +

    np.sum((np.log10(d_pred)-np.log10(d_meas))**2)

    +
    +
    Parameters
    +
      +
    • predicted_dict -- (dict) contains predicted data

    • +
    • meas_df -- (pd.DataFrame) contains experimental data

    • +
    +
    +
    Returns
    +

    (float) log mean squared error between predicted and measured

    +
    +
    +
    + +
    +
    +parity_plot(compared_value=None, c_data=None, c_label=None, plot_title=None, save_path=None, print_r_squared=False, data_labels=None, legend=True)[source]
    +

    Parity plot between measured and predicted compared_value. +Default compared value is {ES_1}_aq_eq

    +
    +
    Parameters
    +
      +
    • compared_value -- (str) Quantity to compare predicted and +experimental data. Can be any column containing "eq" in exp_df i.e. +h_eq, z_eq, {ES}_d_eq, etc.

    • +
    • plot_title --

      (str or boolean)

      +
      +
      If None (default): Plot title will be generated from compared_value

      Recommend to just explore. If h_eq, plot_title is +"H^+ eq conc".

      +
      +
      +

      If str: Plot title will be plot_title string

      +

      If "False": No plot title

      +

    • +
    • c_data -- (list or np.ndarray) data for color axis

    • +
    • c_label -- (str) label for color axis

    • +
    • save_path -- (str) save path for parity plot

    • +
    • print_r_squared -- (boolean) To plot or not to plot r-squared +value. Prints 2 places past decimal

    • +
    • data_labels -- labels for the data such as paper's name where +experiment is pulled from.

    • +
    • legend -- whether to display legend for data_labels. Has no +effect if data_labels is None

    • +
    +
    +
    Return fig, ax
    +

    returns the figure and axes objects

    +
    +
    +
    + +
    +
    +static plot_3d_data(x_data, y_data, z_data, c_data=None, x_label=None, y_label=None, z_label=None, c_label=None)[source]
    +

    THis is for plotting 3d scatter plots. +We suggest use matplotlib's ax.scatter to make 3d plots.

    +
    +
    Parameters
    +
      +
    • x_data -- (list) list of data for x axis

    • +
    • y_data -- (list) list of data for y axis

    • +
    • z_data -- (list) list of data for z axis

    • +
    • c_data -- (list) list of data for color axis

    • +
    • x_label -- (str) label for x axis

    • +
    • y_label -- (str) label for y axis

    • +
    • z_label -- (str) label for z axis

    • +
    • c_label -- (str) label for color axis

    • +
    +
    +
    Returns
    +

    +
    +
    +
    + +
    +
    +r_squared(compared_value=None)[source]
    +

    r-squared value comparing measured and predicted compared value

    +

    Closer to 1, the better the model's predictions.

    +
    +
    Parameters
    +

    compared_value -- (str) Quantity to compare predicted and +experimental data. Can be any column containing "eq" in exp_df i.e. +h_eq, z_eq, {ES}_d_eq, etc. default is {ES}_aq_eq

    +
    +
    +
    + +
    +
    +static scipy_minimize(objective, x_guess, optimizer_kwargs=None)[source]
    +

    The default optimizer for LLEPE

    +

    Uses scipy.minimize

    +

    By default, options are

    +
    default_kwargs= {"method": 'SLSQP',
    +                "bounds": [(1e-1, 1e1)]*len(x_guess),
    +                "constraints": (),
    +                "options": {'disp': True,
    +                            'maxiter': 1000,
    +                            'ftol': 1e-6}}
    +
    +
    +
    +
    Parameters
    +
      +
    • objective -- (func) the objective function

    • +
    • x_guess -- (np.ndarray) the initial guess (always 1)

    • +
    • optimizer_kwargs -- (dict) dictionary of options for minimize

    • +
    +
    +
    Returns
    +

    ((np.ndarray, float)) Optimized parameters, +objective_function value

    +
    +
    +
    + +
    +
    +set_aq_solvent_name(aq_solvent_name)[source]
    +

    Change aq_solvent_name to input aq_solvent_name

    +
    +
    Parameters
    +

    aq_solvent_name -- (str) name of aqueous solvent in xml file

    +
    +
    +
    + +
    +
    +set_aq_solvent_rho(aq_solvent_rho)[source]
    +

    Changes aqueous solvent density (g/L) to input aq_solvent_rho

    +
    +
    Parameters
    +

    aq_solvent_rho -- (float) density of aqueous solvent

    +
    +
    +
    + +
    +
    +set_complex_names(complex_names)[source]
    +

    Change complex names list to input complex_names

    +
    +
    Parameters
    +

    complex_names -- (list) names of complexes in xml file.

    +
    +
    +
    + +
    +
    +set_custom_objects_dict(custom_objects_dict)[source]
    +

    Sets the custom_objects_dict

    +
    +
    Parameters
    +

    custom_objects_dict -- (dict) dictionary containing information +about about custom objects from user

    +
    +
    +
    + +
    +
    +set_dependant_params_dict(dependant_params_dict)[source]
    +

    Sets the dependant_params_dict

    +
    +
    Parameters
    +

    dependant_params_dict -- (dict) dictionary containing information +about parameters dependant on opt_dict

    +
    +
    +
    + +
    +
    +set_diluant_name(diluant_name)[source]
    +

    Change diluant_name to input diluant_name

    +
    +
    Parameters
    +

    diluant_name -- (str) name of diluant in xml file

    +
    +
    +
    + +
    +
    +set_diluant_rho(diluant_rho)[source]
    +

    Changes diluant density (g/L) to input diluant_rho

    +
    +
    Parameters
    +

    diluant_rho -- (float) density of diluant

    +
    +
    +
    + +
    +
    +set_exp_df(exp_data)[source]
    +

    Changes the experimental DataFrame to input exp_csv_filename data +and renames columns to internal LLEPE names

    +

    h_i, h_eq, z_i, z_eq, {ES}_aq_i, {ES}_aq_eq, {ES}_d

    +

    See class docstring on "exp_csv_filename" for further explanations.

    +
    +
    Parameters
    +

    exp_data -- (str or pd.DataFrame) +file name/path or DataFrame for experimental data csv

    +
    +
    +
    + +
    +
    +set_extractant_name(extractant_name)[source]
    +

    Change extractant_name to input extractant_name +:param extractant_name: (str) name of extractant in xml file

    +
    + +
    +
    +set_extractant_rho(extractant_rho)[source]
    +

    Changes extractant density (g/L) to input extractant_rho

    +
    +
    Parameters
    +

    extractant_rho -- (float) density of extractant

    +
    +
    +
    + +
    +
    +set_extracted_species_ion_names(extracted_species_ion_names)[source]
    +
    +
    Change list of extracted species ion names to input

    extracted_species_ion_names

    +
    +
    +
    +
    Parameters
    +

    extracted_species_ion_names -- (list) names of extracted species +ions in xml file

    +
    +
    +
    + +
    +
    +set_extracted_species_list(extracted_species_list)[source]
    +
    +
    Change list of extracted species ion names to input

    extracted_species_ion_names

    +
    +
    +
    +
    Parameters
    +

    extracted_species_list -- (list) names of extracted species in +xml file

    +
    +
    +
    + +
    +
    +set_in_moles(feed_vol)[source]
    +

    Function that initializes mole fractions to input feed_vol

    +

    This function is called at initialization

    +

    Sets in_moles to a pd.DataFrame containing initial mole fractions

    +

    Columns for species and rows for different experiments

    +

    This function also calls update_predicted_dict

    +
    +
    Parameters
    +

    feed_vol -- (float) feed volume of mixture (L)

    +
    +
    +
    + +
    +
    +set_objective_function(objective_function)[source]
    +

    Change objective function to input objective_function.

    +
    +

    See class docstring on "objective_function" for instructions

    +
    +
    +
    Parameters
    +

    objective_function -- (func) Objective function to quantify +error between model and experimental data

    +
    +
    +
    + +
    +
    +set_opt_dict(opt_dict)[source]
    +

    Change the dictionary to input opt_dict.

    +

    opt_dict specifies species parameters to be updated to +fit model to data

    +

    See class docstring on "opt_dict" for more information.

    +
    +
    Parameters
    +

    opt_dict -- (dict) dictionary containing info about which +species parameters are updated to fit model to experimental data

    +
    +
    +
    + +
    +
    +set_optimizer(optimizer)[source]
    +

    Change optimizer function to input optimizer.

    +
    +

    See class docstring on "optimizer" for instructions

    +
    +
    +
    Parameters
    +

    optimizer -- (func) Optimizer function to minimize objective +function

    +
    +
    +
    + +
    +
    +set_phases(phases_xml_filename, phase_names)[source]
    +

    Change list of Cantera solutions by inputting +new xml file name and phase names

    +

    Also runs set_in_moles to set feed volume to 1 L

    +
    +
    Parameters
    +
      +
    • phases_xml_filename -- (str) xml file with parameters +for equilibrium calc

    • +
    • phase_names -- (list) names of phases in xml file

    • +
    +
    +
    +
    + +
    +
    +set_temp_xml_file_path(temp_xml_file_path)[source]
    +

    Changes temporary xml file path to input temp_xml_file_path.

    +

    This xml file is a duplicate of the phases_xml_file name and is +modified during the optimization process to avoid changing the original +xml file.

    +
    +
    Parameters
    +

    temp_xml_file_path -- (str) path to temporary xml file.

    +
    +
    +
    + +
    +
    +update_custom_objects_dict(info_dict)[source]
    +

    updates internal custom_objects_dict with info_dict

    +
    +
    Parameters
    +

    info_dict -- Requires an identical structure to opt_dict +Ignores items with keys containing "custom_object_name"

    +
    +
    Returns
    +

    None

    +
    +
    +
    + +
    +
    +update_predicted_dict(phases_xml_filename=None, phase_names=None)[source]
    +

    Function that computes the predicted equilibrium concentrations +the fed phases_xml_filename parameters predicts given the initial +mole fractions set by in_moles()

    +
    +
    Parameters
    +
      +
    • phases_xml_filename -- (str)xml file with parameters +for equilibrium calc. If None, the +current phases_xml_filename is used.

    • +
    • phase_names -- (list) names of phases in xml file. +If None, the current phases_names is used.

    • +
    +
    +
    +
    + +
    +
    +update_xml(info_dict, phases_xml_filename=None, dependant_params_dict=None)[source]
    +

    updates xml file with info_dict

    +
    +
    Parameters
    +
      +
    • info_dict -- (dict) Requires an identical structure to opt_dict +Ignores items with keys containing "custom_object_name"

    • +
    • phases_xml_filename -- (str) xml filename if editing other xml +If None, the current xml will be modified and the internal +Cantera phases will be refreshed to the new values.

    • +
    • dependant_params_dict -- (dict) dictionary containing information +about parameters dependant on info_dict

    • +
    +
    +
    +
    + +
    + +
    +
    + + +
    + +
    + + +
    +
    + +
    + +
    + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/_build/html/modules/reeps.html b/docs/_build/html/modules/reeps.html index 9275abb..8f4ad8e 100644 --- a/docs/_build/html/modules/reeps.html +++ b/docs/_build/html/modules/reeps.html @@ -34,8 +34,7 @@ - - + @@ -87,13 +86,6 @@
  • Installation
  • Getting Started
  • -

    Searchers

    - @@ -157,8 +149,8 @@
    -
    -

    LLEPE

    +
    +

    LLEPE

    Parameters

    @@ -170,7 +162,7 @@

    The order in which the extracted species (ES) appear in the csv file must be the same order as they appear in the xml, complex_names and extracted_species_ion_names.

    -

    For example, say in exp_csv_filename's csv, ES_1 is Nd ES_2 is Pr, +

    For example, say in exp_data, ES_1 is Nd ES_2 is Pr, and

    aq_solvent_name = 'H2O(L)'
     extractant_name = '(HA)2(org)'
    @@ -178,7 +170,7 @@ and

    Then:

    -

    The csvs column ordering must be:

    +

    The exp_data column ordering must be (names do not matter):

    [h_i, h_eq, z_i, z_eq, Nd_aq_i, Nd_aq_eq, Nd_d_eq, Pr_aq_i, Pr_aq_eq, Pr_d_eq]

    The aqueous speciesArray must be @@ -718,7 +710,9 @@ effect if data_labels is None

    static plot_3d_data(x_data, y_data, z_data, c_data=None, x_label=None, y_label=None, z_label=None, c_label=None)[source]
    -
    +

    THis is for plotting 3d scatter plots. +We suggest use matplotlib's ax.scatter to make 3d plots.

    +
    Parameters
    • x_data -- (list) list of data for x axis

    • @@ -1077,19 +1071,12 @@ about parameters dependant on info_dict