From 0513aabb615ccc92ac8c9b0866c66afda5ac5137 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Wed, 2 Feb 2022 15:28:39 -0500 Subject: [PATCH 01/46] update LM3 modules (needs testing) --- .../LepMap3/DataParser$PosteriorParser.class | Bin 2525 -> 2529 bytes software/LepMap3/DataParser$VCFParser.class | Bin 9452 -> 9451 bytes software/LepMap3/DataParser.class | Bin 11979 -> 11984 bytes software/LepMap3/Error.class | Bin 23325 -> 24135 bytes software/LepMap3/Family2.class | Bin 24561 -> 24812 bytes software/LepMap3/Filtering2.class | Bin 15798 -> 15792 bytes software/LepMap3/GammaFunction.class | Bin 2336 -> 2335 bytes software/LepMap3/IBD.class | Bin 16443 -> 16442 bytes software/LepMap3/Input.class | Bin 5202 -> 5201 bytes software/LepMap3/JoinSingles2All.class | Bin 3476 -> 3511 bytes software/LepMap3/LMPlot.class | Bin 10558 -> 10593 bytes .../LepMap3/Misc$ArrayIndexComparator.class | Bin 1800 -> 1800 bytes software/LepMap3/Misc$KthSmallest.class | Bin 946 -> 946 bytes software/LepMap3/Misc.class | Bin 8962 -> 9630 bytes software/LepMap3/Order.class | Bin 17692 -> 18116 bytes .../LepMap3/OrderFinder$MergeRunner.class | Bin 0 -> 2112 bytes .../LepMap3/OrderFinder$PhysicalFamily.class | Bin 3047 -> 3437 bytes .../LepMap3/OrderFinder$PolishRunner.class | Bin 0 -> 2035 bytes ...nder$SingleFamily$RecombinationScale.class | Bin 845 -> 845 bytes ...der$SingleFamily$RecombinationScale1.class | Bin 3848 -> 3852 bytes ...der$SingleFamily$RecombinationScale2.class | Bin 4280 -> 4284 bytes .../LepMap3/OrderFinder$SingleFamily.class | Bin 21905 -> 23919 bytes software/LepMap3/OrderFinder.class | Bin 31624 -> 39404 bytes software/LepMap3/OrderMarkers2.class | Bin 6060 -> 6756 bytes software/LepMap3/ParameterParser.class | Bin 7275 -> 7078 bytes software/LepMap3/ParentCall2.class | Bin 25527 -> 25524 bytes software/LepMap3/Pileup2Likelihoods.class | Bin 13976 -> 14053 bytes software/LepMap3/Pileup2Likelihoods2.class | Bin 0 -> 15913 bytes software/LepMap3/QTL.class | Bin 10536 -> 10535 bytes .../LepMap3/Separate2$JoinSinglesThread.class | Bin 3329 -> 3329 bytes ...parate2$JoinSinglesThreadMaxDistance.class | Bin 3287 -> 3283 bytes .../Separate2$SeparateIdenticalThread.class | Bin 3548 -> 3547 bytes .../LepMap3/Separate2$SeparateThread.class | Bin 3289 -> 3416 bytes software/LepMap3/Separate2.class | Bin 30521 -> 30747 bytes software/LepMap3/SeparateChromosomes2.class | Bin 4422 -> 4506 bytes software/LepMap3/ShortPath.class | Bin 10253 -> 10252 bytes software/LepMap3/UnionFind.class | Bin 1705 -> 1705 bytes software/LepMap3/scripts/LICENSE | 674 ++++++++++++++++++ software/LepMap3/scripts/map2genotypes.awk | 4 +- software/LepMap3/scripts/somePaternal.awk | 26 + 40 files changed, 702 insertions(+), 2 deletions(-) create mode 100644 software/LepMap3/OrderFinder$MergeRunner.class create mode 100644 software/LepMap3/OrderFinder$PolishRunner.class create mode 100644 software/LepMap3/Pileup2Likelihoods2.class create mode 100644 software/LepMap3/scripts/LICENSE create mode 100644 software/LepMap3/scripts/somePaternal.awk diff --git a/software/LepMap3/DataParser$PosteriorParser.class b/software/LepMap3/DataParser$PosteriorParser.class index af24204c70821bca596eb5b89da0389fe65467b8..4970103b0f5e9702decb3a09798fb36834420a2c 100644 GIT binary patch delta 38 ucmcaB{7`tqYBol%$!pkDg@qVc7#Nrw8914o82Ff+8HAY}H$Pze%LD+xI|%gv delta 34 qcmaDTd{=nGYBol<$!pkD`S}=_85o!x8914o82Ff+H$P$f%LD+V?Fg0t diff --git a/software/LepMap3/DataParser$VCFParser.class b/software/LepMap3/DataParser$VCFParser.class index a9c9a5b30a698767c61e1c2ddaca84a8f89284fa..d65d91a15a59d4a9de13aed4f5b31322d5c7aa21 100644 GIT binary patch delta 29 lcmaFk`Py@Xs4U~=&0?~LS()E6=xn~Dbc>OZZ}JWm69BVV3km=L delta 30 mcmaFu`NngDs4U}_&0?~LS(!gD=x)BFbc>ORn`!cP6%zow>kAA3 diff --git a/software/LepMap3/DataParser.class b/software/LepMap3/DataParser.class index 0dfdb7735ac02d226f72c60b16c0fdf947d957f9..35d5dd62ed126b9db188adde8b32c74cfbccb212 100644 GIT binary patch delta 735 zcmXAmSxD7U5XQgx&h>v?u5@w9de9a%O|cR;kX{6-EbXa8FcV2A(!?yY61hDI(L)cn z2wN~M+SC-3GEqn>C2TRxcX4c}D~uNq!HbCJC1kJ``MipKyoLk3jw;?jHE-e+Z{aK(P|rp*vI)(+jZQYB zn=N?6HVp6%-tsPfu^nT)2M;?Wh@BF{E{S8er0{{H^Pyz2M|QJU_VclvVV|7m6PMKS zsWkDKbo06NvtI@|AR~N9vXB0J&82+9<$T8#e9zVVz-WGCEI%=xpP9%nOyyT*Y0Tj_ z=JGp>ImBxI;1!PW6-PP9F|GX8>>mzuT+aljImua08KLX1aq^!D=CoPI8MBGACQ}R9 zoG=9jx+kNXvYArjWy+0ZDvdX`=xXmHaNa*;V5{DgWW8*dH$y2wi3@a4$-rjzN*Nrm z!EOpAUG4IMuPKm2TD4rLR`0Ul3%jnylEVvh4o5Jf)Z~+%i{mhCF`p~DeFTjXwhDv2tg?-A%^%-P?}nPw0#ux5eY33 zW?@8@*-Kb^C>lu*E3M2ZiXt(o#F8GS)hf}bALq`QxiiCXPJeN4acjRvyn*MQ3~{>Q zh;WBTc_O_2u%w>2G}zz`iLhY3?(iZH zpdS_<~9QC4pU%$ZkpJU0Kb0lFbLQl|53*M{FK5;ugb3aGez%T6M7zg-G$G&U(4{iI&QI2c&OWz6p<)m@&w{i22No3Hh;greZ zgvsT&DKJp|j4Eg`<;H4u8k?y!4s1}#?;vul48t)JIV&!oi1C6e#us2v)>ka#YQBSou z;&a(mPi*+$@8aE~gLO5YP{1FlE{giX-l#tmjJWQYRH~|X)&*4ov;0ACK&_Q?cvRK@ z_dIoqqe6#zO13)DKqb~iSU_D?KN=|~-G+q}NH4~N)Rvy5T?Cdz7t=@RY&M&nuUSIF zZFVfBX*MgC(Mnq(R?tb?Sgaz8eL5bc2D<}9JMFnxLqFPwp_vp%1)iX}jykNPZbuH* zQ)-3_Pt&XnH#XAq8RM{-e#t1qb2Mhe9JJ7$5w~L-{W+pRyB(~S7G@f#Y2;MAKo>?% z((VGgo4TCIR55BCcGKojMcNpc`>M+pr^CxM%9)Ecia5)$kB&J@2eyNi)kS=s>iUR( zrZ*sZWEcH$sA{U%uq1uuTtmv zqt$AqKN9r@XZqAU5y`I3sFx9^+N>jWILIq8(K+YMqyK+xrJC#d_wrP}KCLocgdC#7 z>(oE9Li;9|i+90DXPsJFn>h$as3Wsb`!?8E?lw_MR!?fqyshyau!1Y^x-1x|=lkT^ zRS;5xp{N?>yQ0PyuJWm&aHTJ-dIMFVFt4RXP4&&M^@aU4e2;kp{CAIndHxZJ_vz~O zpGd(_SPg`DQ`OpAHS(XZ$EhOAg)Ul`WyJ^7nKc$4(hb=|agwsKD{zXMv(s>f z4rSZ%DP7EVYd_cHEal{s;9FXfQ;qNGmz*^GNH^tnG!BR`EsJAJc{Rygx`*{^XDO4D zZfRo!Ev;-&D@$u-R?Bw&VJS=a;NrKB^F3T=ah=Jviff25q;YNFI`pazXDo@|uyT7a z*L1EKTy0!ObG36VVX=E$YoJkXqVsu|6O4O!0!b4o-))KY9Mc0D!*c4A?@lmG5dN_A zNAok$__MHA^3+KFYbEX#__)LuuHz$f_D0%Xn-bh9if;@1Km5Aaw1MB|lOia1)v)%lQh-1AN>tVt8*u{y-TH~DL6No0CN1Z{3xrSRY&ydUi zFZxd&-1v~O9E*)nJZxNvrN*sz#CQnHjGthI@e)>=`e2nQ4XaHBc+@l%WSWo1Oifs0 z+JUvE!)P|0#p9;Sc+zaZI5=HEI@~{82go_ zIG{A)pt1&UC>!vmvJpp=HoUF0qmw1_y45S^2s1JhxA=*IFYqM;i&OUDD^41m;1nB!Gcq$h&w{c1E1AhGdayWy!Nh=W{y>AD{sO60gsK1l delta 1631 zcmZwHdr(wW90&08-G|t&BD%ZxE<1{jni|+X03l#k0~A-I&`iX~I&HZkjXJP^<8z2H zM#+5OPbE!Le1tDBx{9G;_`+xAJ1tC`rWvOioTc1(LvW(l+)j?6vI-mB=Wiwjh;YP zRM3342g_)$+lflL?e=2@_4j08C51e%V>PvU;!#D9JYEnb#Sg`LdNV$NYC0ZYij9<< zkcJPaIKhW4)R2>@K;MDAZ6|Vh(mtQHnw}iBqwQ{z}Y7EsY(v$gl^jknhb(cZN;J zKJq3_G}M9R%I&_A?50~uHXNY#q%npDu*}YMJTxj#_Tms7OP*pl4Ce1R-V4cSqVH1z zI7;cM8EB?=QvHU{!N&8SZT2DIP*G_?I8@v(G%q;2G*}YK3C{?Hhf;g$ARMPdX+E5! zU(=j8P5r$YI71$gOUm|T8m@H1RU+R@xJ`F`v+xc1hsWX$ z6%TJV^{HV7rf1CFg;GbvQr5n6zU=#MYYqotZBNaHB_I~ zqU&)L}Zhwx+hP()BhHFyTtgRn6&#q!9OIU1t0UOe64D150sfV^Tbiscd`Zc z!)!oYBTKiy!M__>thm9vSll4HVlEPtT`|uTlwC1T{cnQa|2sjND3z@;yPxvej=QLD zb~KEXk{zQn{XmmNI86((WAaS9c!33j#2!o=rH4qbl$Lull}itm`BG`8bOCqAncSUQ z>O74pM|+UHL>C!xSytzX>WEX?!kkBlG@g5!Gd64G;|oB0UUtxruR64t3BTy5ZK8vs zavd}>H$1+URmkcVF#}s5eL{MU^eO3}bgT44>9f*fq|ZzHq%TtQBo&wF#-zTHmmBzB zLC;K#8J=K-rpvP!Y{i!AD6zDo)Y=vEtocv8eoi@0v!ZroVY$aH3TZKy7ZoF$d zf%j}}SZRBRRZ0|AE3sIkjKo@HI!Kw1b;??-SN3Cr(u!*39yThEu}QUJv+Bea)sL;} z8`!2U#&)#~JJsdbrLI7&x(>V5YV1|FVV}AKb?Sc9s|WD0`e`m2)T7YUQ#h!eL8E#Z zht+FnQrmDuy^Ch`K8~qBp~ViIup4obnfclJb-W9hjVXM@-wL>f>kLe9*W(744mT+y zkk=#G{O`xcp21hNClKej#hWHO_OkwcUgQ&s bMZ56@7)$S16gRnzOmDR+4EUB0bh!Hu(hljC diff --git a/software/LepMap3/Family2.class b/software/LepMap3/Family2.class index 9517aa3a80b0db1b262cf026b3288740cf505aac..f7a7dadef87bd7fae25c81e147deb9e12c208926 100644 GIT binary patch delta 8836 zcmcgx2Xs``*53P^Gnq+dk{LoKnIscZC?OLdg_a3IAfW{U5s(rBBoQ%afG7f@@EE|T zs37G^Q$Vnx0xt*%_{3fius*@|ET}vMMHG1mO3DA7JCk5}uK%sK{`F5*?%k)iZ-0BA zeNR5e$6w%x{TKiCED?3&i%TO8*_&mX7dwY@3%)pPy;7BIE7P<&mGO1hZP(~ zMf@U&xwV7aaQgO(jwnv<&1CIcHTjxJH>E^SKKFG{51Of^{toKJ#YSdSu5BO_=i7LY zF(E3OON|wH4l(vd74R^_>Kw%*jY{Xp)=a8x?~D-5c_LHPwN*2!vL;ncsm>Zy zHEp7UujLx!kh5E2HQ(UiX{?ySrdCb4s&=xT(+mes<|#%eSNqJ_4!(*1!endPyLWMM zaYk?5XTZU8xrRwjuEO+~=R5cot}_<8yj^c|@aJ%IQll6wK=dT^~6CX9+YM$BV8|+m#y{Yd zW8)w3%C+&ojFgxT>YRhW+&(nsY@-;S0)zwtxnCwL{oK~}Bh)!BACF>FudWqlAVEEz_c5y4C zqD>2V8&w%|+l&pQaU6GI4-ex69Bm>`VK3KmGT#W$1vnLFlE#a<6|d&jd_Ms9FsJh- zZp%+_2Jd1YKf~?#Mee|#ayEa%Io!axB8EGP6z(Kiac9w%yNGt&RdnNgQNZ0qPwpW~ zxIk2KPcefF17bcGiQBlhxRd*cyScAe%f(^?4-gOV<>F}`A`bE}ag2wH<2+KF=24=7 z%cPA*%V;i_@jOO)d92Liak7vrWFM}S{rO5cl*h~ATrI13qO9Rb@GDaQAwT8m@@u|Pp5d7)jBiqIo~Dv{o@&i?DvK8>Ki^GVh<)DYIS%#gWC7e6%0AVX6~-N45Y!(n5LZ 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 Z3wyed^3)>4-v;d(SP!t*E;S&Tw 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 TY7XV8O6~R1M0{@qu?YGv>{lpL diff --git a/software/LepMap3/Filtering2.class b/software/LepMap3/Filtering2.class index aaa172b9874890be354fd42ae19629265394be2e..c2aabb4c3660fee0866c4c2a253c860c59996578 100644 GIT binary patch delta 54 zcmdm1y`g%8v>s#eW*NO$W@b?a;mI|IZ<+rysBA7UYG!2Y-~7c`i-kFbL1eO`g)tK= K>*ffHI934i01#pT delta 60 zcmdl`y{&qKv>s#mW*NO$W@bqSk;yfNZ`uDaC^G+NVA`Bx)Xc~@aq|abEf(f92C>Pq P7RD@0K(IN;B90XRR8$dR diff --git a/software/LepMap3/GammaFunction.class b/software/LepMap3/GammaFunction.class index dc5b8e1bd148bb30e0b2376eb665d86975144521..bd257e521dde3b573aa318d94fc33c80337fb19e 100644 GIT binary patch delta 30 mcmZ1=G+$^#2rJ`(&7rKS%*?YGxF*+fL@@tnu$+98!yN#UlL>GD delta 31 ncmbO)v_NP>2rJ{k&7rKS%*?YHxF^?gM6j6sXJDFqgToyFndS*{ diff --git a/software/LepMap3/IBD.class b/software/LepMap3/IBD.class index c7cfb65d9f4e03ab6eea7781c9c611b68e494de5..f64f7963830832aa1a4ae8059dd70cfb7ceb49b5 100644 GIT binary patch delta 30 mcmdnpz__b{al;vF#-*FjT1PW8doplO*0#4`Y$%L&_Uy4k;VVJuZ^)p~2aR_hf7cY^;wtnnlkH{HGGyytnp^PQ`$w_2UWKeKZ{4_~I8 zCi|3=>Q48j5A^I$_c~O$9pesLgm|Sm)?c`gDOJV_<-uZvIl=1c?5fYq$GFb}!991r zR6bEE504Zp!p62UnM^j@*IyV-9m?l}to^lV!Dcz=h*YCLaY%rz&-ybV zBQs2e%(g^Z+;)G!zZ-aNM3M2p0TLvhlJ!wGo6gGqHx?C*2Rk2Yu4|; z6bW~gW}Cf|uCWu#=7SbCN1(=cBL8d4Qm-oTJ4kw)hS#Rnx;7t0t<5!}IO`B~YI>w~ bMxD|;w%bf}ud=7{@Wd}{z1}BX&hYs!$0vH- delta 479 zcmZXQK}%F|6vcn{J>~!Ay_uAU3w0<7F$z?i@!1HeD`5}~g2-T68I57e6eKQY;|Fjh zuf@0!1V6zbOrm9(X*E`6Wwj}&m7hVh={mJ*b2;bUbI$+pe;j`lcgw%mHh^xv72O&$ zV0U$PIjjqAG7+SL-tNmm$|cX!C>uNzynFX2a@Qwvcc-!s1^?o$!NHM{^wqHk$t#)6 zP&jVB*K8X0!nBi*{KBIitc8`bGisS8uiYQ@SHqOo5pVZz`)ehpR#`6bq^JdKT%8{Y z!f9`JHe(eSQ#+LKPa}BFrm{2E952{HXk>twY-4f9>S2dhiVmg74PIl5NP#zsYNc6b zDJwG4Dl_czmWT|=HQuS$m7651DV}6$=Dj9IWs$Z?hS|fD13msl^WyM9Em2vbrD}=E z57tSuPt59MQmb!iy*@3L)mu$sM;QH@FZ!~@*FS&ZZJhuB diff --git a/software/LepMap3/LMPlot.class b/software/LepMap3/LMPlot.class index 0702bbfe06343ac2d90227462ea2d0881bc1f2eb..d91ed8ca295b7b958c64c60945de603050c12cea 100644 GIT binary patch delta 46 zcmdlN^e|{cnl$73&FRuxIhlVlm`#>f4Pw-rT&x)_jPn}u^T0}I1e22O@;lb^8UbM650b})_jPn}u^e0}I1K22O@Wlb^8Ub1nh$mN5u3ET0_1+90?ZD87b)k6|5yD3DfU Q*f9AmYb)c{$;E6_0XPy8LjV8( diff --git a/software/LepMap3/Misc$KthSmallest.class b/software/LepMap3/Misc$KthSmallest.class index 7779ff5a0701825fc2a71e1e6b8d8d38cd2fbd0b..300a279162f76297af93465d5d7c1d166c862205 100644 GIT binary patch delta 114 zcmdnQzKMOq0!A)Ieg+msK?Y7np~-_co{_}voPKEm0)0Flw=TLlwuHPlwpu! zlx9$8lx5Ijlw;5Z(pHS}433Nn3|@?i3=xb<42g`&3@MB%4Cz4id5r1|Rg4-8t&Exs Q6B)G_W-@9|)?sb~0H#S0E&u=k delta 114 zcmWN|Jq`g;007|`_Qzt8ST=D1iEsg(R-%+>B@&$j=;`HEI<-pnI*ua5R?~b_rB(WR z*ycmTmyBO{x{V>%5H;SLh{Tw}gwhrhQ>JFj>SIxNX@`|v*5>T&vA55`0ms^(9CEhc O;)t7L?v`QRy#4`eVi7k0 diff --git a/software/LepMap3/Misc.class b/software/LepMap3/Misc.class index 0e6c4a0a0dcb59efd4de1b3b08ea91f88dd286a5..165162c16dd00390c6db21a01be5ced8ea0c83c8 100644 GIT binary patch literal 9630 zcma)B3t(H-dH#=dr7OwTuh?=d+sYe9mQ7+iiNTiR#ED5_<2-Hha)2nd;wY9SBgu&q z9tCP}!y{wt5eTHzl-9H>Bz1~o8tMWYjCC)%wGA8XplqyVr9juwt`N|^|J*BCanuza zzUQ8M&VT;@``<_3`}EsWM6^uoSIHzu*%*vZ#Zca%&?!0S7s0 zwn{~Ua;N!R8v+rHipeFYs3#B$Mgu*~NwOVre|#_|iR`wMpi+%;DNiMLQq(jt98^Yg zHJV4W1X&y9>AYouMi#R2$3+@lLFHgB92knXN5Z|5ZW}N1Xrz*j7kQaNB^dU!Rcz-^ zS88M@t9A7v0FHkgR71;Dsuh$oEyG5C{D4NwX$2^TBA}~yS}Iz2_f;BpWbvq8BTXOq zG|HwNl~xKeb+uSmy(tMx%FPni^)#tE`4J6Vp!E50qJ2l!x(6NJYZHWv@zU)}9o0p(MPemr9 z%hCyL3`BduVY4S~O>;ErKhhqI#eEqk)5%RQ6=d%S_J(0GQPA7p*x8_OPICueQ^r#o zJ3D<%8IRJxWIRv*q>>+wFzr$)b{w>q=JKB&joJtd!4X;=Foa%>uA}YzC8*Is>cbac zCCKS%HO@(AnAi1d6s8E)`Fr4*PD6zBRTc0#Q>X?XGvdV`VX@{YpCiKfALP{2eMsV0RKVyQJi}HxSk6&*;y= z_aT31FtB+)khxSF#xfr(@F9)gCfx_?!@@6!GG^a|U(uAPV{QoSLQ99)rjTtF0jsA>?W?r8W zW@G7RJC9v!T%)t}Etm*gt2q=BULDw7v8! zJLk8h!KBHhXI~$uK6AVuW~&rUt#k6my+ z#G-a1%bS8jZ11mX^!L0E*`Mhr2m5dc0)ea&;PN@xcy0VZN53DKF$eHE-SuBXvgXXP zDDAU@F3`Vns(sxE5eSGE=qDQekp2z4!1>_K0#r=@Ohi#{91Tdzl=JJbeH|y>UuyIoy$|K}2N6I( zwv~hTujtn*{dcnVkbc&2Bo+@KK>rVY2=M%zx4LEE^JWOst!b(^EJSC-A~%ACaNux< zOxUI{hf&Eq>)RfOw6TFmjQtqqum{H8D#RKw18QfdE&ZSEhrP+>r*}B`nIboSG;S zvjI*#qJNwj5Dh90;S!~)C`l&mOR>;I88d8=X!6oInwZN4r7CAaumzJJr4%FF0;>Wz z3mJw*EM!Z1`ZkVJDF@1vo4K5UY4`itqKZ6Smx+UDp$aefma6DSO}TVUu(u`99fVKW zL>07Hy`cJybN03HX>mqoA@(VkXx!Vog9yWZT4Q$O?9AnAJ6!Sj(M+EjG9hhRkir5<|l? z#7t(-nbX$i%Ts|&Y~Td^09;zMY4lxso=5E(eM$))ZPMrntzcHRFe_U*oUpFo)xG?^ zlY`8UYy!|(01@{@ia*+mFjJ60CtM?C(gy;N=@4SG=@25XITC`=nqqybKqPf^L znE{N7?g3p6Mh)%i3FbP5RlAKnzqj?NMj zRCtn9WsDT_St>bCWxLMNoU=54oaRr^!n3rPr^adVIjT5IRfV)_{N#u8=kcc>BX=9ObqKTZv0rO&Ay=B6>4$1k<+viOL5q>!4rOGhHTleBu0 z)@~a%3wP;g;-{s`(Z*quC@Xm)@d{R#)S7d)eG^QYX&wGs-{<| zp1w~l^fqmvpV77SbJ|Sr&`$a?Wc>%(; zsM0r3E8aGv18T!a&~9qNd@Gr#7p-JFMrPWG6$NC~R*O$*vk>AUA&7hgtOd8CIXAUyqhHNRypaCXKMNaoVEqu?c8-?hGlEQDJDEMz`uB$s^(0PlwYTjBMo3a zI9`B57pBpYJ*}kGN#zEuMUoPc0?j>zZ|QuL%e{P(ojVj`$6{lLijya3$LSfHylI9^pjd}u?&6sa&WbRil`1Ib``wkYFZ1^>LefS z!+V%kN@=#~JktQ(1m3|tMKMCaqXQJf$OKB=6qg250GS6dQb2XLG$IRV@1R4Nv%-kC zNfT4)FzC#a${hf`p%mSr6x|`vW%DZ~OM)F)rM>!pFhcO`L6?YzhFT8dP+EIQLe%O+Qy`fnW$abX(2_@2&=Qij`;?qdb@DwA8dMuISzXH90L# z>uBQraVj}U`HOQ-oT9Ar^f}nT9pBAe9BIp`w_tD9RGq_IXCI@*t}Lg$rd};pbD-*? zVzr{LUMsc~YsDHw>g%qz4BLg%?sSYMUMsd79;Z9|riH76ItV^(0@O~Zz=h{RM3SrE zdp>wx6O^!xI&iJgNjJh5LU=m_6|wLe00$(Y5vZR1=N9?`gh&1dRL3wfgTh+6M~<3k zwS>lk{U!3N3VPXeFTQG0b#-J-AQG!8H6o4+fhQb2fz>8`Dgpbm^Uyr~eI9G|Rzxm6 zXm2ynz5P;j^BLXh`!9v}z0U}*hvBWh|1;rx;rHOX2k2Z6W4Qs+Tu+P8D_sqzX@n!J zq8=a{K)BdXe#Fu+-iIidhVSjb_kM}*Z3ezYDSR7YiaNflsM)}`Glj4J_uzZn!0$Lw zwt+8C8TcMg;fwF{8DCTw2EH%jWIK2nDaq5GAd5JoG$wp>u#Hiv%i>gOoi>-% zX+yAjo*tZ_uWlZ;38$@OH1YPZB6!Q!1f5L0?6Pn?)gx6K@(WQv)E`Eqi6GMtVCE+H zX%sObhM<2FErH=KLoBHSz)c7R9ds0CeKU3(#q2Hkdjx6lHVOS6T}&CLxRSV4#woO4 zcz#1hu|mMi3D69ho%9su%%Hqg&MBbZic=p!_@$5QMZJ(lWMlcik3FLGeI|j!#JW!P2`T}~(Cwuy|Tri5LH#>pDGVFjr zxVFKudb&9y)6D}ooc;M`L!<4SZj>*RE2-fsOnh@kB~o8#N97qVUs}Co6Z8y}w8;w% zHz+QJ6>A+Mx67JRti@$9L*bK^E(`DEFH(PRc*}U2%NagtH1SO3v_99!itJ9X|9Omt zaVLHkikZ9NIwR2KF%$&%ARpaJ4RHM}aE~2y9Kzg(9PN<%T1=l#Wzpc|+!SJ-aQCWFG>b599Qc zR0yl}(4*k^6vFo-$ajxPBz4)>!_prC8x~y7bCzb0SRpOlgq_O_kMO{tM(9PLz_PcH zl7Q(WC!b`aMfoRfSK4iAQl%GC(Tk8?n$zXSSK_6tR{oh9DK8u8?K@sGT~*AI{J(+B{w6AlpF-xh z()g&B`JXUbNdEvM()loxa3L_^Ud~{n8Xvd>x0WBCq<@@QZGR{U>aBv+AZSOUlLT?d z0MRQ!EWI2=Zmg0x>SLUh4&}$7a1l^`0w_PFIVdB!=+U9{rl9oBfHDhE{!@yJW`m1t zU2&@+2@VCBv~ELK(+Fp$2xl9F?exznNR|Ot*_A*ub$w_KXOhZFL&Z)%km|N`lW$0>gU>af8X@%F-a(oD|wy z3OAmlpG|j-I=5FN$JM8ivr8h(tz8n<$-QHcy8s8b;#(8<+zzkXz1irlf6EhBxX}re zy3s%#-&?16-An2$Ubok&EZK}dPK#VyHk$bHbl2^L(US0?8ft`CKFSv>@w;vlEflNB zBUaN=(TpqOmH3Twt>jU!nwwHQHW@r}cgrD_5zIBhM*;e8y~tPyBjn;iL2%AS>S~1) z99-?BT*T2Qr?0W8`t!oK{%hxS;bw+2yoM3hOzMRUmREMBaoboDjw6-_iA}qlpVxG$Gt9 zr+YMUZrV2Iz@bR&p*(@EL)-v~uBTRUBW`E*&<^2GQ^ih5$B8`;9mx-*3ofEkF)w9j zK2G9UWZ|kam8em7;ysnRdHw|?7jEOy7qakz@8|dm=K~ln944di_Nh9XywmWw>`t|& zJ}G=LgkLaIc)qf5+Ww&MJrF(s;rB!M0EF*_@I4S-Z?W|7#=_^Mgy-<43lDjvlQkxV z7gw&OhU7MJZ75|>nBC)q`21H zI7YJ|Ghd^MjT2(?un?trxW#xc&(VlWQxv{qq*b{m#kS-H>4!OO@;YU`(^54lI_j;( z){2K7B@Ij#i0dZAN`u>CYdXxiPRmFiZd+5_6!L6#CufSLbr%J=niowesI47Kb{ZcH}SjZl(-weR*%q=c%BhopmA}GCdEDUyttSC zTHHskiu>s|;yC?I9K$bD4@k8QaK^?@7CXdFs4a_n>6q9Br4-Q`u^aCuTy55gE{qg( zs>L$=2vqI|jU;jk*9e@TO{m)QWjZm_IBkWy^L5NKP(P;_3o7Y)gn}%nnUhEz*-_}I zqT*xPnLt=Gsp1B-k2#-EKHImAt;ND~c*8GfsETHrKSB>?LMTBgBlaSc5E7J0?1O*w Mz~3+686e010cy^CB>(^b delta 4901 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 HP4N8(A%>R$ diff --git a/software/LepMap3/Order.class b/software/LepMap3/Order.class index d8bc83a28499b23448476abee5f6944402ecd267..f3a136b7564063592138682c4743b1079ccea089 100644 GIT binary patch literal 18116 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 K5&lBcPWoR=@_7va diff --git a/software/LepMap3/OrderFinder$MergeRunner.class b/software/LepMap3/OrderFinder$MergeRunner.class new file mode 100644 index 0000000000000000000000000000000000000000..6aab1bbe9853b692cefdb2f9c9a16e8ddfb57619 GIT binary patch literal 2112 zcmb_cO>7fK6#mBRcr%+Ngakvt2_)e!HWUK^myjPwlad&m1V~8vZQ0mM*buMLdfh_R zUMf@xsXg~ZB~THEa;elLsE4ZO9ynC$u?LPlRBF#v>wdE~v5`=bs#@~CH*ene=6f^W z%#VM6_dS3-fV0v%N3P)Gb}>DV@HYuA6urr}&(}mEJnQTBTQa z{vXPRM+-SyLST!DRR#F#LcDMtY_z`h@jWBfx}4YI3mziE4a4fCf-!gaMAhD zR+;@Kj)5FC)}n-a)A7nV)oFh~$B@8=n)s_)GcbtbCQhh&jgITjSxY55QvRKnj6VZo zIH}`pf#w==((-SZ7{>$`%v+RpLvK1AC*?7mGI6>Rcep2$nAR~R&{U^V6K8OikYcu= zu2KLUqptG~_3l!_m77R1EIm_65=>zHc5n~Ew+h9B?45>`H5vl>UpM1h*_)Hrt?H2) zGli0uwNEL`M}^i8s^AdR4dedu6>%tK!j#lA&b;#+KA!5f3L-7DxD zdJJE=u@xNs0fQ?T9^QzN{;-ycXfaOg$UVhqN~gCgoC^Zio+6!UjOr=*7(I-)cShw3 zGW|xo(H{QdOEk3`{o$xgMea%wmC?qp%D*tHGZu|Z(*|qDIv$iie~eE!?v8cjUa@pT z$BkGnoMXaE1nR>73yqFz56U0cj_Ff$^O%nEHR?x;Jcc$ofF3!BJ#q*MIgFG%j&XSc zQ*s2C z&Q+>BTdVTZT9xN(RjyEW6EOZln-FEbW>SaXPg5Tt7Aj*Cv>?U{S!U|LC9t=vtQW$& Pc#qa#*MhInHH7{FzgE9t literal 0 HcmV?d00001 diff --git a/software/LepMap3/OrderFinder$PhysicalFamily.class b/software/LepMap3/OrderFinder$PhysicalFamily.class index 0e56c8e7f0a73b0215f69067bc0b6d5a14b58027..7bce76d29bf4d9414a023e94a72cbfecbdd5341a 100644 GIT binary patch literal 3437 zcmaJ^TXPfV8GgP`dywU|Fad#y8#-yfBz)4^&ZR|y|WL7eX z(?gnuCTTA^x#&%rnWlI;lg_j=p3WqgX=gIM>r8(@ulqAPnHKuKyDMug1ZKSZou2pb zywCTp`RC)megj|#ztSNEUYvIdmNR3Q>Dha2b+c;c&C-myW|uZ~C<2{#%zNfQ$tbVK+3^I$l-#qES`<*GDg~i+ZGMmb37yr%H0k%D8;7ri=Spk_R2V#~7G*i|q zRi0Esa{ytxTw63t8&-s*^{Rm*c#*B|=f`ye zKLm+VGYf^uQmKiZsY-;z~Vz5+io zZ~@dEjy=}`v%>y8aPtOgf{1)Gk@Sz6Da~|u7VhliwoBu7+9CF6 zr-Npb?nz)@I`cK6=^b>Y#n(9S7Z((xXOzv!Luiv-h|>EItudG&IE*g$99=EXoERpT zVOsk%{S~5*5LbSW_|bzn_Q5fJ{N}e%quburasM4x{($_RBXc}^|C14wG4$4AYD_CU z!l@BGru%juVBbOX`Id^?fpJX#xah496n9-kw<)F&IG+oY5o8?gGJzwq8*%v(j?1Iy zmpvGl$1p9wgPc5$McIpGc>-m53LnW7el0U@?h}+D<>%+lqqFp>W?;K@G{pW$%&ZJFg=P(&q-1Vjy+H!1B9CG#eh@CJEl$3+@5DQ}TiPfCmo6NyZYfzV?_MM#|c7Tsin zZpAJ&rc2GM6eEjli&)8gGomRY`j{BM zE%muM$h{uitEj(v?!_#{$im+y;}eMyrTcTl*;lt0LHrSl4^jCH+WpXz zn8G`5X%ak&DNh|zM08Xo8<{ROn2&EQaC73GNYsGM(+y0#E~lUyM!U*VyVOR9IZG0T zd>x9sL8ZQl9=Svv<_)|o-^6A47Ou!!n3uQljx_P1T*jYeA#k0xcWK&2l?uo-ZTlrx z@F&!0Ekp$U5gRm8=<{1}i?7zp`ac{OqW8b(4-I#6x4-9?^(3*(W!2J|9lX|{^NjOLjE6&`MI_L literal 3047 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 literal 0 HcmV?d00001 diff --git a/software/LepMap3/OrderFinder$SingleFamily$RecombinationScale.class b/software/LepMap3/OrderFinder$SingleFamily$RecombinationScale.class index 939ca88acda8ad4c7bffcc51081d348376ba2d1e..31f95a2f89249433af15da233a3d0ab4e94f399d 100644 GIT binary patch delta 19 ZcmX@hc9w0!5hlj&$w!%Dfuu6C7647c2G0Nh delta 19 ZcmX@hc9w0!5hg~@$w!%Dfuu6C763`221@_{ diff --git a/software/LepMap3/OrderFinder$SingleFamily$RecombinationScale1.class b/software/LepMap3/OrderFinder$SingleFamily$RecombinationScale1.class index 7e200afcff5c3ceea1ff15f93d776598bad04ad7..45cbf52736031f19c5b4a298a1489f70f3572ca3 100644 GIT binary patch delta 413 zcmWO2O-NK>0EOZA8_(4-Q&ezK3x$XVA_~M51x134u!I2-glunHDFPwUm5ad9i=n8P zRF?Lmc-1UyMlD1u!G(#2e`PK-6-!Jq!M^o5a2`%=>iyK-u+L<<5ns?`xJgj~bJ5F= ziuajJ?~2o6OqIJDT@W}!EGR^r>Pb`sDRMcfM$+_vvufgkn#oYeyB6a&*IR9+jXbq; zSq~{t2Zee>u^v;VC)`#iclDG?J)>G()Tx_hJ*QPK=+sNP^ooA<5V84$`n(SRRg>FK zj0W&)&X@L(~WLirsYlWZs#=gFrXswz!{V<7I^D>X$ zGr@H$+aOt+_KPh}X`6KISkW)5`E4h5xv4!W^v6o}Y1Ci3b-Hh&?y?12* delta 409 zcmWO2U1-g59LMqZ@9b?mj5W#y7gM7YqK>(+A}N#P*vXnaY?|i{B{P=8W)kyI;&|RX z=V`?BALhcDB;2T7xKeWAj%&Gb;dlN1*5}ptY*la7U@;XI6h>0HkSRD8pcEak4I(t0MFW2$SIW&^WqWS(o2`m%IbwLazzEVhYdHdE&& zR@y>?n`v?j9d4!5ZRFifk2^T$POjNXzim8l7mwY|kb4-3{j++EKuM`^W(1F?5|JefVN7;-0=;>if^Wv-`K>S-E1 zLz@@kS%G6-~FQ^TV6`_7;D=9ZJ0u%Do#Zy_eLE?+UqxQTB*Q z4#Yn^q1vZpd=^E|qh>Hpyr9{Hc88+mC6|27fWy4<4PSiASBrf29lw0fA3yTXPld4+ Lqaj=PR+0G+Mmc3I diff --git a/software/LepMap3/OrderFinder$SingleFamily$RecombinationScale2.class b/software/LepMap3/OrderFinder$SingleFamily$RecombinationScale2.class index 36c807e947cee0a5ac70a07f665034393bd58a50..33c39374435779f8c7e079655c3a4109a1250884 100644 GIT binary patch delta 507 zcmWNNVQ9>89L7K2J5RIDZP&ChjJq{vhuv+vZB}?88*Mf_8@5a{LsAHhHcaTo9JBc^ zZCJ7wqP?I_c%c;KjgmJCue?}!p`?^oYJM-DdY-4x^Sp|@jy#+7&8s!aycS#P`TbRv z@M`^~R-Nhg*Z8a^b4QbAV_rO~HC9dl6eOS=LdqpyA(kkQ6cX;KO8TFE}GqEDraXfnXd-NzLgSf zW1|`!PZP~*rcEt$Y6m@P<*1VMYbV##Mq2HTX%`Q)n`!M~MjgD+Uf!vTPcHXW-I>9_ zH%s=o3l0!gFEu*Ib{(QuhZ)c@QaaA4PB5jDZq_ND>ojlG$48xU@w5C;KYuj9oX%N} z&Rc;l*m4bKo&?jDyzHp2kf#*I8gdU`WrK!k)O9+PX1{K5L?c|%EpBR*N4m`$d3?|~ evzp+yCi$m(u6D`__0Wnm?WJ<(dX4g1vjD;))Aeh zh?EPhq^ZAfp%mq2tjYTni?K0gtma0||MIEN^XYjIyC1tf8n8OYvP`>MlnDkCt~S#U zTSZvrp`_&+NBRj@TE@*;g*u7`-;mW&1{-eW%|hxMx3jVn2Ch zlf1QGjMx+t_M1uj!;DQkXEQFx{) f)h?#An}16Aw|iZQx?GvMGiUN=12&L3TTuHST~%_C diff --git a/software/LepMap3/OrderFinder$SingleFamily.class b/software/LepMap3/OrderFinder$SingleFamily.class index 5344feaae35ca22dcd1091e29ae3c95330a92334..e79be2a7ce23cfb4b510315d2b8b72425ecdc506 100644 GIT binary patch literal 23919 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 PNL>Gp`^mU|PR9QOx{zDa literal 21905 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 diff --git a/software/LepMap3/OrderFinder.class b/software/LepMap3/OrderFinder.class index 620b72c0c1ba946a9ca381aa8516222dc1bd6dff..96c392264a84ef0558d9d722af5c57cdd092b460 100644 GIT binary patch literal 39404 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 diff --git a/software/LepMap3/OrderMarkers2.class b/software/LepMap3/OrderMarkers2.class index 1a6f8faf09e14adca3bb8582083e4c76050d58f9..43a89c8d498fa97ba3422a85b9806675abe60bf8 100644 GIT binary patch delta 3867 zcmb7H3wTrY75|<4$W7BrX>Xd6kOC1DTe+nzj{*gGC?LhQREi)KkU&G5+9owgf#M@5 z2%?DS^+i?iwbV{U0e3-Br*7h84`17KW8xH@n~$m6a88l#{8MOsefz%UYj4i~ywC5P z|4H&t@w;PO2aiATG=R}`%P!Y?WwaCR1XpXcIUH}C6KQUD;!=V;($-RwW>q9Yv0x$` zjl^9j!sR-yK+?KhIo)xkj;pZFdS0o?zZx4gY#?Mc1y=@(n}d;t;>u{GLB}=NWRn{&UfNQP9qQtM(PFkOc$<$rQ>GY!m14? zRpo-h*)Di-n~u|QyR}n2WpD@X)NltOyDxZcdpr?p(Qy~HTOX?dy4%WfcpdlRehv3o zro%hqM;OXpTVvr!qB)|Wlc3It)rVp(q>!QE$9$e1Oaw={(1i>q9wtzU6OZs)sSAGW z(y<$RtWAz#qf_{a4jT^6o$EO_8V|NC3kx^R4NqEcIr8ME z@l)#y$7#tFp4IUjJcNF%Sl!k#uQ3)1*2kUrIY%oKo(|}EK`3~76skh8hEQKuoOqEH zInxw=spD5dF|SAQ`~6GdH#%Oz%iJkesST~3Hen@qx1pyAb)pHca_J=_OG>!T-{SWg ze)lb4r$^hCHHUP(jyJ3awE|}ff7EdZf3gl~LyW`RNj7E66#lH^ZM#hkon zl{zPLvG42nEB@9K|NMBUrm;O9ULI`bpam7v3+D^d4|RMbQWSXflpv;3NdB(l6CueK z^+hYAY}liafew78;~&^gkVCPU6aOMqw8g`bhCu6y5P_CpICgO;7HEye!|8brgd>4O zV~7QDCS!ILk?S1gGd9 zzqk=y$tg7K^ox+2e0bvliPTjO2k!W0$gR45e1Sj}p04aL%G94JL2bTY+I=CVj|*+<{- z@=BqRIu%n12jNRTKiJ$BicgDIhT{oBp*2;_weBW|rDbL(X%vmohyiz}FM3-d+*~{@ z77MluWu3-S8J8YTZ>wmGFF`?JWuIbC$Y^Y0CXJ_ZjV3U_-tAz`#PCMa+RVS*Pq0>yh z^3S@w{#iOzh=QFb3l=WV)v1!ItU#t|bkQ6gbj+X&td30o@H&Dww32}gCPEdF`tZtdeOs_O-FRN%DO#k{Vp?Jy${bvr zqNU;xgsp_2nAly4H)XA5D#1F$P&TgRX%|l_Y~-($)U#hlOkaS`weu;Tt6?L z6;^w{%ataLuCTuBx60+3iVOq*^*QU(tZF*dI+*oha$)vRV|DGq>|w^5S{s)wFpVa% zvGxGmC#Zc`zYo`TV$gng7mnD6fqM}!w%RytA8xX7tBu0S-Pk6C#ts&ZUaZqF~BsYev_{i+1OiT8ncAn+I@J! z#xs1JZT#%y@p(S3F@ACKc#w}5^m(xHYtvX{ypk5bHo;*UOL`AZ(^%Sja3xJ+S?@_N z@;bauugk03_yc>aH{R^=VB;-5u9QWDBb*M?ijXn=nvK7h#_GP<+u$rxY<$qm3^r!M{U;hIL z@5fQb`rqZMPxbY;@kIwRa%ki}WcySbUs>5Xp6n;e47tqRiQzugNz3v~W3HP#C;H~~DPFHl1AK~U z)b*v)0o`MCK$AP6aM*#S(by+s_(d**Oe6L!CL>RQBA?rv>*XLdy`w|JxU`JK^``KThKa&%j^q=W_i#5=y;IXA1sQLPz)8rjf8| z`gSmoOq|oBX47obDC-MV=J)v1`7XAp+BBx8dzo($TMgg+#Y`ioE~GF=ujzl zsK_(OrW($3USFQIzWf9575Uh~S!u3#JZjT?YlPdE6c;JYFx|AU&%aF)Gzw?XSukl4Mp6tVv<9WLjz4ENU<}=gv2+*8=wXbbT^LW#@Tz+W<#ZSm z=@Xnu$1qvSz!WJDQ>9^;CKcmsX)#3k}(#N-Yn{Pel z5p^4OtM_A%x+jUp)Mv35ncR}`7$QCvC`?Vh8u< zaGlUVQ7>QS7vP(69HWFS|Itm>@w`BCcsIDxA2#UKWPg-e=;r?d!UN(j delta 3315 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 delta 1950 zcmZux3v5$W82UGUX6{ z*6ZlE}rRIybo) zm}Uby1Jm4OFi>v;J_hRDlr8*OA)i#1k^w21BaRtED9C|A%9r7gl+ZzB8h8fv?3W7I zFNMYJSz%A1f!$!G$mT^^-41?Ze)idX6Y4n()DYG%QA2SLhRV5Pv+(bC7-ONp!R_KM zEmOqo+=$nWZ1@>qNVMe^BzHrF4$y#36xViOWTjGCW+A1l!`jj^valiWf`yk_u{5)C zTUZ&`xCQN%s;OEiVqU;hOx0;&_zrBbux)Ave-865D7uur%o~=j_cA&V@ zZMu_E<7s%DqqP((xlDIPDQscXR7qVzSe4!z)LK!&d0a9<3dPRRY6`g~dkv~=a=Tm6W!_9oW&U5LW8j(g#gtBbP{l zkH;Ap$m4u?uz-8@0gS+dsKi1{z#?3a#kdI%;Z{6?1RllxSi;fAu$=hg_WKO13%z_h2W_R5mmTZ}7~;wywpScncbvE5zH3x$!d3u4WaLk3n(r{B%60 zEoG66Ty?M9u3ounmdl7#vq%B+mm%vcCUDE~FU_L52IAI}PX^?X4@KLCnnIz8#*GFS}4!k4s^6O~3n3F%8$7l2J8z<}I z;7Ud9nmJI-^?8?T;lxCYw@ab$Kaj_)Vf^gGu3r8wadRl_?81BE-cX_P{#JY_)`s%w z6Y)x@WX%4l3l$0~LA5P?^fC)yJ%uUlI1o5!;YVIdGNDpQVVJ3$Z~yQlCJ4V74E`c9 zyU+JAWZ^f*7W^)%%&7NIoZwG#<9VDEDYGJZ5`VEBS^FAjY4FLnOL9u%zO?I{n`Y}; zA4t3zrvP6XMgQX#rI7E}ZJ?(E z^UDI~EW|B>g@^eg*|-elkWv?eK`j&X^c(bMf?53r2gqPj=1f`S|4+``-kkje z^Lle$6l9NC7KJPt$ksE9LA{H?7RkjXyNl!%a5)SfMNL7aIfVBFd}SV|p?s0@%}zy< HM#cXE=qt;c diff --git a/software/LepMap3/ParentCall2.class b/software/LepMap3/ParentCall2.class index 0bb8a36c10107f988b78b098e5f89dcc1019c613..cd7f580814ba3cc6bc0e88ceff1bf0b80ff223bb 100644 GIT binary patch delta 77 zcmV-T0J8tL#{sm*0kFh10X(zCHH!-fumCpy0JQ)hlhH#;lTShkv#UW50Rn0Pv%Nyh j0R~wA02cr(lhH#>lR!laldweyv+zU@0|6wn0Z6tRgMS;+ delta 76 zcmV-S0JHzJ#{sv;0kFh10Y9_FHH!-fvj92&0JQ)hlkr4KlTShkv#UW50Rv|N0<*kA i%mD{g0{;LP04$U7L|BuqMF_L*L=OW3Ap*1hN46VcRU7;O diff --git a/software/LepMap3/Pileup2Likelihoods.class b/software/LepMap3/Pileup2Likelihoods.class index 144e44839e376e44734cb1cb0e20f74f88a0b06e..3dbf7ad50aa569f2d2b02c7c6cd14885b9087fb2 100644 GIT binary patch delta 4315 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 ND=m*83|=aP`9JYn(6;~p delta 4186 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 C#l%Md diff --git a/software/LepMap3/Pileup2Likelihoods2.class b/software/LepMap3/Pileup2Likelihoods2.class new file mode 100644 index 0000000000000000000000000000000000000000..25e9209497c34ebef2c6c68c057131a05f3f196c GIT binary patch literal 15913 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 literal 0 HcmV?d00001 diff --git a/software/LepMap3/QTL.class b/software/LepMap3/QTL.class index 6dd6e7fa77c58ccb6561576e51f2fd12affb968e..ea939a1c3db312c95129dbe14cacd9ec9dcf002a 100644 GIT binary patch delta 29 lcmZ1xv^;3TMFGZbn=c8}Ffm&)a83>sR$`3WTq?X-6acDk3JU-L delta 30 mcmZ1;v?6H3MFGa`n=c8}Ffm&(a7_*rR$>Zg+FT;MSrh=Wq6!TF diff --git a/software/LepMap3/Separate2$JoinSinglesThread.class b/software/LepMap3/Separate2$JoinSinglesThread.class index 93baa372e0b1c73957f95ce56a9af2dfe94cb0c4..20ff226848f1ef6a894a703d2b5f4d593adc2a1f 100644 GIT binary patch delta 233 zcmWN`yDP*20KoC@_o7ZNiwrI@$RH&K{0Bx^luQ!n;UePrqeCD3>a{mggeZ*%Y4pj?h|;xo`+=2aN-eJ zPkHu?FVFe$fYIT{uYAw#hcAA{ H3nRb(v$R8n delta 233 zcmWN^JuAck0Knnj@1jmFiwrI@$RH&Kd;_B_N+ydz`2ZH_2Ar2;IL_M{?&@$}ul{jz z?uaBpl-X#tS@CR7EEvnz(BdL(F419vZkOqECHM+YhNhE* zT&2ns5!Y#QgH|``a*H0f88FSD8HUUdH~#{)9|KlF&eV~Uy&pU%$fGlyiW$Ey!XXtU;Xec ISsVHJ1B}5y@c;k- diff --git a/software/LepMap3/Separate2$JoinSinglesThreadMaxDistance.class b/software/LepMap3/Separate2$JoinSinglesThreadMaxDistance.class index 4372a16761247c4c0612c852e89d591f8fb63326..15fab55388a933ecff9420baf82c26d052480bf1 100644 GIT binary patch literal 3283 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 B{_6k$ diff --git a/software/LepMap3/Separate2$SeparateIdenticalThread.class b/software/LepMap3/Separate2$SeparateIdenticalThread.class index f3be6ae577680572f778f1b05b804d5da88f7d49..c491b2266f9599dfc92deb98282e066b7226b10a 100644 GIT binary patch delta 341 zcmWN{&nv@m9LMq3`|~o5S%__ADHh|T#uVjXPJVNs{h%={`LUt-UC3`DzrM9)Qk07u zeUJVDrR3y7ilg!exXAbPI6R;4&Nt`X6~&`p)2pOfdo`8YYqHUfWDZj37MLT$!_h>d z%&X;R7D}z|&is?&HkjCoEH)#L?Xa-}MeIZgyWnQG<}(MytzVb}YOM!m_9CBt6fj^+ zLAV$~1^eJ-Kbjau3kT4{L4+8=h+&*Vm|+z29K|ZfaL93_Ie|+~;)XHYGmZyN;fd3D zW&$6a#W&~hWBk8dlngG3#n8r(xWZf!H`kr&4RY2cRlxh+xdNQ}F(z&%;!zU&yD z@jz~kuJcG9c`V;NQRJzz8ajBSN|{uZOeqh~RUOl+kr(=lC0F*4a~VIz|I1a$;+j|ti|H5V6eHr|hSV}D&D@d}#$=S+66TJqa95&?ONAJ^sBDIICRGK~s)k3Zp2w<*C(6T9{mqgm%)qW?tW#2}d2M!4 ITF|!t4|&Tm#HXMsAG;!HrF@ll6(v@V=kA-e# z{E$Wy6hT3~3J%n*1Dwjo9IyrqQGb}27=QW4FZ@U_5f#*kIuNA32gG}G-{(2cdEfWE z_q^wPQT6#e^W&TEz6ao5oSrf->7vn|(LY*Q(i@K_ve|`;1s^dts)xAqPuBFpPy zdahV3nplmeE$qZI0(z=HEkJ=-tS)BaVeGc>ES|H^$*S;P3(sQ_rx@6pQR&tuvO~$i zYzbZzFsl19=|qi*$MKSdm$A>*XjO<(y~CXm0WGCWoQ8_9{)iU&6WZm^SS>H1Q(i`o`~~aeuh=Mm zL!bN|TjUiC$Um@6{)wFY3s1@!?2uQni_wjN>BnRsW4Qx#bx= z6_peCRJp2|0ychtcmath-FE7CQvs={;d2|K*v?vmZwwY=qY_d|a2sPNRf=yEk1+bZ zDrQ%6yJN0{hs*aLLO?n%#0GMU%~UT2s6ph&DfUuC*tA3(prvA*qUh!#H^Wp7 z!y2sRktXukk8%ijMI5g%(-063VT75EHu!OXnM4OF@gQ>L-0!4t;ovPXk8^mH8DSD9 z@ES7%7tqQ3V8Ma)oW35`4Va>c6)+qSw;sT{rsSab2TekhU&p!yEL=rREtkR9k8Lf- ze!|P@jB9wm`S6SuaLA*#Ou%=0+|Lm5)UyqH>I-<&uC_d#>~!>=_x}$#I>ot6H1m6> zM?v-bw2XVxg4xuHyBQm41)_8xmQfq-rlTZ4;m9=qT(Y;)cV5sKv81?M4-;a=smgfpZTAeIdjgO z^VubzU+lbl?$y_U1w23I91og#-pSxQo{?!ace!k1wxP85#-40vKEJ9n-`9OpKEgeQ z(2R{exz3pmw{ow`ecbQ0N~^a+DyHsqd5p&mk?XU5P*tNhE-j&5JQ?N*@1)c$+U@7G z-96b&4mk#0h8Q+TU#_bsr}yR?XBwx?;A-<|awTWRNY& z)g^~;73+Osvroiih!USk4$`5RO87CiH-|0?dDlAeDp{UMj#HP3$vE|yb^CSy*J%;z znXbqTQJE>S)!XeXc9dx@k{_O|_9mQkVc4HgZt(ymFLQIn-9@&K=?w_7T;&d`z@8;w z&k?qhI06YVq$NO|1X&`MHW5}UULzs065$4k(k~9%q=c8m<*-cQZ7Jn_DdVV=Ga*ws zCKa5JN=_Bz0?tUxL?mv?Bw^Ar-84$XTq!e5yUa8lcG7f9mFbmgbBoM21xd3)3+o{2 zA2lmkrB=Xf;Q`7qB+L>XR4qW-H1LpWS|lkRR?V`MX{=FA=;t-vT)B2yul0*~=gGsg z^9U{>)@jl?xMy(8B$pEkW6pozp2iKFWuYmc6zNae%6<7Lg zI>@6%27)c4B>y$L6z0ThRo297ivjj{gKoTCokr%q@qfU`nC5b5*708}GHN4pwFR}5 wNgdUS^)inpsb`VQ=PFsiQpFY0;Dx6&Cic2|2%{PvU?0yaI-1h3p63Ys1K9%Os{jB1 diff --git a/software/LepMap3/Separate2.class b/software/LepMap3/Separate2.class index 1c9670ca7fcf3d746cfe5ee76f25eb62a7f87f6d..28e70f4577928c5772271cdb92df575d14831847 100644 GIT binary patch literal 30747 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 literal 30521 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 {)yO8&*@#s+(zYjhk3?$3ghJHwr#M;hJ1?gq`C; z_a=v)|Hp~LYKs5=###7(H+WZo;e4G8%hp87wQ4A79Yg)B$uz*4LdRIg(gN!^T4YV7 zC3s(D&7ib3lh#|u)A`mcx*pGVYc}19i;fSX-H)uf^prJ^p0(!F3)TYqyS0cuv=-Ao z>tx|ri^V`|si?G;iILWFG2U7sW>}|+xk#UEtrRP)Rbs7`5=}^-Wvv$HT6N-U)*5k< zwN_kWtrJ&S>&13!gSf>yL)>XKh+TO86gIeLtY+~mYoqv$b*A{e)gs=oTE)MuHnHE@ zB!zXRj9X{PG1eA2**aTJx6YAE@LX+uNv^ZLY&0_0oxQ<7-sA>j!2^?!8+r=+0CYs0LjAqext^UZf_qO-89Q4<+>TPVi$(E;tQ%b`7mkM= zu9XW%+P7sLN@L=;o^>rQC&NOc^G|X6F&` zMvBitrdE%9pP5Dsmk#JeO!f%-N;}0M*xB@qKsdu$#AJqhG0`7-7+_6vv<_N5HX*S<*3-{~;MQ$vl%=!$r z9hiLzIJ}Wf3YpD)y8*f>%SJC#jOC!O8lLR{>_1JbT=tLJ8DG~O*voKWeVr`pd_$%O zd|g+r1-v3JjQbY;7jch3pMZj(V~JPpLn8AtzekvEVBbcQdQiCucfuzbUAu+(5-fk- zHM2>-j3#IF*(BY&N&k!{4SfRkT5rIHWHf1%O&(w)5rx2?c8OOF$CvezSq?5rJ=l$& zEHlx|GMYBajlrA(G*G|Zqm}thNXj#LzOqHO;#mbEeoD4Ml8lHHR_H9mINXNk7Chg; N^K8^yjORHd{~r=|u_OQh diff --git a/software/LepMap3/SeparateChromosomes2.class b/software/LepMap3/SeparateChromosomes2.class index 98367c0e1f36f6bd5fa1810d865b00d44dd5f799..e3289f2b19eae4cc741c0fd4ef27ae067e4da6dc 100644 GIT binary patch delta 2204 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 diff --git a/software/LepMap3/ShortPath.class b/software/LepMap3/ShortPath.class index fccee924dbdd4a6df9d3dc614df14a98b5748db2..0bf46d3bbcfbb22e1224776fdd6a8ffaf58295d2 100644 GIT binary patch delta 28 jcmeAT=m}V$%*ef&QMsLoxtW1?a<^(Mqx9xis>)0Ndn^ba delta 29 kcmeAP=nYt)%*eBuQMsLoxrKpma<^(MlLXV|m#WH40D+GPApigX diff --git a/software/LepMap3/UnionFind.class b/software/LepMap3/UnionFind.class index 2bac91143ffad7ee5f48dbd29cdc95f6ca77fbbe..e375b1bcaf58d171723c18df0202bd6dc560a17c 100644 GIT binary patch delta 45 xcmZ3W2WJ2P diff --git a/software/LepMap3/scripts/LICENSE b/software/LepMap3/scripts/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/software/LepMap3/scripts/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/software/LepMap3/scripts/map2genotypes.awk b/software/LepMap3/scripts/map2genotypes.awk index 64cb5f3..a587300 100644 --- a/software/LepMap3/scripts/map2genotypes.awk +++ b/software/LepMap3/scripts/map2genotypes.awk @@ -1,7 +1,7 @@ #converts phased data to "genotypes" #usage: -#java ... OrderMarkers2 ... outputPhasedData=1 > order_with_phase_software/LepMap3.txt -#awk [-vchr=X] [-vfullData=1] -f map2genotypes.awk order_with_phase_software/LepMap3.txt +#java ... OrderMarkers2 ... outputPhasedData=1 > order_with_phase_LM3.txt +#awk [-vchr=X] [-vfullData=1] -f map2genotypes.awk order_with_phase_LM3.txt #output columns marker name, chr, male postion, female postion, genotypes coded as "1 1", "1 2", "2 2" and 0 as missing #providing fullData ouputs parents and pedigree... BEGIN{ diff --git a/software/LepMap3/scripts/somePaternal.awk b/software/LepMap3/scripts/somePaternal.awk new file mode 100644 index 0000000..51234b1 --- /dev/null +++ b/software/LepMap3/scripts/somePaternal.awk @@ -0,0 +1,26 @@ +#flips informative markers based on a map +#awk -f somePaternal map.txt data.call +BEGIN{ + FS="\t" + OFS="\t" +} + +(NR==FNR && /^[^#]/) { + map[++markers] = $1 +} + +(NR!=FNR && FNR<=7){print} + +(NR!=FNR && FNR==2){for (i = 3; i<=NF; ++i) {if (!($i in d)) d[$i] = ++p; f[i]=d[$i]}} +(NR!=FNR && FNR==4){for (i = 3; i<=NF; ++i) if ($i==0) pa[f[i], ++count[f[i]]]=i} + +(NR!=FNR && FNR>7){ + for (j = 1; j <= p; ++j) { + if (map[FNR-7]>0) { + tmp = $(pa[j, 1]) + $(pa[j, 1]) = $(pa[j, 2]) + $(pa[j, 2]) = tmp + } + } + print +} From c5cdb225c2c8332b2876952b98b9c1ec6210f65a Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Wed, 2 Feb 2022 15:38:38 -0500 Subject: [PATCH 02/46] update LA modules (needs testing) --- software/LepAnchor/AltLinks.class | Bin 0 -> 19506 bytes software/LepAnchor/ChainPaf.class | Bin 0 -> 5741 bytes software/LepAnchor/CleanMap$CleanMarker.class | Bin 1086 -> 1086 bytes software/LepAnchor/CleanMap.class | Bin 15036 -> 15324 bytes software/LepAnchor/ContigInterval.class | Bin 9363 -> 9362 bytes .../LepAnchor/CoverageAnalyser$Gaussian.class | Bin 1248 -> 1248 bytes .../LepAnchor/CoverageAnalyser$Zeta.class | Bin 1330 -> 1330 bytes software/LepAnchor/CoverageAnalyser.class | Bin 9179 -> 10628 bytes software/LepAnchor/CoverageHMM.class | Bin 11631 -> 12308 bytes software/LepAnchor/FindContigErrors.class | Bin 12066 -> 12047 bytes software/LepAnchor/Input.class | Bin 5928 -> 5927 bytes software/LepAnchor/InputData.class | Bin 13948 -> 13938 bytes software/LepAnchor/Map2Bed.class | Bin 8487 -> 8486 bytes .../LepAnchor/Misc$ArrayIndexComparator.class | Bin 1800 -> 1800 bytes .../Misc$ArrayIndexComparator2.class | Bin 1989 -> 1989 bytes software/LepAnchor/Misc.class | Bin 3290 -> 4140 bytes software/LepAnchor/ParameterParser.class | Bin 7307 -> 7305 bytes .../PlaceAndOrientContigs$CalculateBest.class | Bin 4010 -> 4062 bytes ...laceAndOrientContigs$LongComparator0.class | Bin 851 -> 851 bytes ...laceAndOrientContigs$LongComparator1.class | Bin 851 -> 851 bytes .../PlaceAndOrientContigs$PossibleError.class | Bin 1844 -> 1844 bytes .../LepAnchor/PlaceAndOrientContigs.class | Bin 125735 -> 127057 bytes software/LepAnchor/Proximity.class | Bin 16701 -> 16693 bytes .../LICENSE => LepAnchor/scripts/COPYING} | 0 software/LepAnchor/scripts/chainpaf.awk | 313 ++ software/LepAnchor/scripts/cutBed.awk | 152 + software/LepAnchor/scripts/cutBed_fixN.awk | 23 + software/LepAnchor/scripts/hic.awk | 33 + software/LepAnchor/scripts/ld.awk | 22 + .../LepAnchor/scripts/lepanchor_wrapper.sh | 268 + .../LepAnchor/scripts/lepanchor_wrapper2.sh | 287 + .../LepAnchor/scripts/lepanchor_wrapper3.sh | 292 + software/LepAnchor/scripts/longread.awk | 72 + software/LepAnchor/scripts/makeagp2.awk | 8 +- software/LepAnchor/scripts/makeagp_full2.awk | 8 +- software/LepAnchor/scripts/normalise.awk | 62 + software/LepAnchor/scripts/paf2chain.awk | 52 + software/LepAnchor/scripts/plot_marey.R | 108 +- software/LepAnchor/scripts/prox10x.awk | 50 + .../LepAnchor/scripts/removeHaplotypes.awk | 5 +- software/LepAnchor/scripts/sortpaf.awk | 7 + software/LepAnchor/scripts/umi.awk | 64 + software/LepAnchor/src/CleanMap.java | 559 -- software/LepAnchor/src/ContigInterval.java | 382 -- software/LepAnchor/src/CoverageAnalyser.java | 420 -- software/LepAnchor/src/CoverageHMM.java | 571 -- software/LepAnchor/src/FindContigErrors.java | 557 -- software/LepAnchor/src/Input.java | 209 - software/LepAnchor/src/InputData.java | 452 -- .../LepAnchor/src/LiftoverHaplotypes.java | 44 - software/LepAnchor/src/Map2Bed.java | 334 -- software/LepAnchor/src/Marker.java | 139 - software/LepAnchor/src/Misc.java | 224 - software/LepAnchor/src/ParameterParser.java | 231 - .../LepAnchor/src/PlaceAndOrientContigs.java | 4821 ----------------- software/LepAnchor/src/Proximity.java | 657 --- software/LepMap3/scripts/affx2post.awk | 83 - software/LepMap3/scripts/allPaternal.awk | 45 - software/LepMap3/scripts/genotypes2post.awk | 81 - software/LepMap3/scripts/loc2genotypes.awk | 102 - software/LepMap3/scripts/locsingle.awk | 8 - software/LepMap3/scripts/map2genotypes.awk | 66 - software/LepMap3/scripts/order2data.awk | 65 - software/LepMap3/scripts/phasematch.awk | 133 - software/LepMap3/scripts/pileup2posterior.awk | 156 - software/LepMap3/scripts/pileupParser2.awk | 90 - software/LepMap3/scripts/somePaternal.awk | 26 - 67 files changed, 1765 insertions(+), 10516 deletions(-) create mode 100644 software/LepAnchor/AltLinks.class create mode 100644 software/LepAnchor/ChainPaf.class rename software/{LepMap3/scripts/LICENSE => LepAnchor/scripts/COPYING} (100%) create mode 100644 software/LepAnchor/scripts/chainpaf.awk create mode 100644 software/LepAnchor/scripts/cutBed.awk create mode 100644 software/LepAnchor/scripts/cutBed_fixN.awk create mode 100644 software/LepAnchor/scripts/hic.awk create mode 100644 software/LepAnchor/scripts/ld.awk create mode 100755 software/LepAnchor/scripts/lepanchor_wrapper.sh create mode 100755 software/LepAnchor/scripts/lepanchor_wrapper2.sh create mode 100755 software/LepAnchor/scripts/lepanchor_wrapper3.sh create mode 100644 software/LepAnchor/scripts/longread.awk create mode 100644 software/LepAnchor/scripts/normalise.awk create mode 100644 software/LepAnchor/scripts/paf2chain.awk create mode 100644 software/LepAnchor/scripts/prox10x.awk create mode 100644 software/LepAnchor/scripts/sortpaf.awk create mode 100644 software/LepAnchor/scripts/umi.awk delete mode 100644 software/LepAnchor/src/CleanMap.java delete mode 100644 software/LepAnchor/src/ContigInterval.java delete mode 100644 software/LepAnchor/src/CoverageAnalyser.java delete mode 100644 software/LepAnchor/src/CoverageHMM.java delete mode 100644 software/LepAnchor/src/FindContigErrors.java delete mode 100644 software/LepAnchor/src/Input.java delete mode 100644 software/LepAnchor/src/InputData.java delete mode 100644 software/LepAnchor/src/LiftoverHaplotypes.java delete mode 100644 software/LepAnchor/src/Map2Bed.java delete mode 100644 software/LepAnchor/src/Marker.java delete mode 100644 software/LepAnchor/src/Misc.java delete mode 100644 software/LepAnchor/src/ParameterParser.java delete mode 100644 software/LepAnchor/src/PlaceAndOrientContigs.java delete mode 100644 software/LepAnchor/src/Proximity.java delete mode 100644 software/LepMap3/scripts/affx2post.awk delete mode 100644 software/LepMap3/scripts/allPaternal.awk delete mode 100644 software/LepMap3/scripts/genotypes2post.awk delete mode 100644 software/LepMap3/scripts/loc2genotypes.awk delete mode 100644 software/LepMap3/scripts/locsingle.awk delete mode 100644 software/LepMap3/scripts/map2genotypes.awk delete mode 100644 software/LepMap3/scripts/order2data.awk delete mode 100644 software/LepMap3/scripts/phasematch.awk delete mode 100644 software/LepMap3/scripts/pileup2posterior.awk delete mode 100644 software/LepMap3/scripts/pileupParser2.awk delete mode 100644 software/LepMap3/scripts/somePaternal.awk diff --git a/software/LepAnchor/AltLinks.class b/software/LepAnchor/AltLinks.class new file mode 100644 index 0000000000000000000000000000000000000000..c52127ad86bd944fce3ea1aeb254aed3aeacd010 GIT binary patch literal 19506 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 literal 0 HcmV?d00001 diff --git a/software/LepAnchor/ChainPaf.class b/software/LepAnchor/ChainPaf.class new file mode 100644 index 0000000000000000000000000000000000000000..44b003d403465b0d2bc7b4db438494a4a07571fb GIT binary patch literal 5741 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 literal 0 HcmV?d00001 diff --git a/software/LepAnchor/CleanMap$CleanMarker.class b/software/LepAnchor/CleanMap$CleanMarker.class index f79445d5f775dec18b4d0d15a3bb342c5d216349..51cf7be445058da45ff3c840ee73ae5ef24281d1 100644 GIT binary patch delta 89 zcmdnTv5#YeCzGfn11EzM10RDkg9w8QgA{`+g93xwF)fvM;jDBVf)+h#6hUm$AnB7_9fUI~ROLnp2BYx(S3++AH43(Ws_@0&>@uB-nKP40cSoO{milzZ-d zFV7x&@L?hv%U^4xFzM5p!=cu>p*D?FrqokHYeS=(L#>UY=dU~^T;IWz+!AVE6K z+!=LKKx8_Tw&0{0b#?RWn3UysOrAUC_>*cEp?gv7{CNw}5N>ITMw=q7i$W`#!<9_7 z<#ODB<|?L);!rdiZtuXzDEJYB8?eGM2sg35xv90OV-n_;l`mmZr$rjVF7i^AMn0yL zK5?ftgNQ-dl*8m{2uGXR!wpjsWEXaXIyxZ%h%9pwQ=UQTl!5tknp(s2I$KtT+a-@o z>2o6Wq2?u__9nqbyfF#$>(>C|__Jf8WXeT0DmJK)OhL26AUiokt;`_Jss#*klEX2% z>$LZNc4y}4qO-yv7r8}kj6nt^iCUFG$<~A;3`(I?(H1l)jZz_T$Lc0?X@{v@46FsjO+so+#ai~V>C*)* zTiZ$X@;B(I9k2l?q&{wK0J&^VANmVB+M8M%Cx8Wwjsb(Rr?aD}dGyRsbTyE3(F`gU za@887lS8A~pdd#zMMh8UT(v3;ebt3S4dHf;=0XpNzGIr2!&bYC=Fofr_W~w!h{em7 zxM&vDi4hASZF~gCThk|$FVSf+Y}M~{(Q&j?qvIiJZEIU+he69|Ig`;G2{lL+*G1Mb zP3faBkiiLu3tnzDNr|Vlw}&>&fxAs`(F!_AqZ7eRA0WNm2AxbH;M*96mzq#tKeCqd zPS6Mn=;zZG%sN36wrMl#T(puJ4O&f2OpfrWouOuEZvZrLI_1lBS_5e@XZ~ClwNON( zRyeejetM`QWKbKO%H(d@uprbP4bO?RHV&c`*KqkP7e%R4Bk*i#Sq7Wh){byvxZR+& z1myHKK?H5zL!(w$!v=#+6O6m-Bdr~wrq<}}@CE@`vFQA{L1)mJ!uUEt4;!Euz%H|Upi0pgzYvH7cn4-C*+ z90lk-N*5V)v7n(hwRT9y(&$%AnF%j0j#orzcF|_KRG95$z_hH-(P!1x!l48vmm742 zz{(bCXmHV0bhSpmL41`)CGnpd>R4^i7TSt2EurXW=q|cjU?js-q+KA|pMVvq2Dej=kGS?0kJ!1t3%Ow7%G6hQ((W>poi#TSRy=e zK_rS;7=d#@>$9AI;!%Shqu=2<+z$Rs%kB_W7FejYheItBbb1_Nzist~XcKl1e_NbL z)Zfw>?eMP*`DH^aq1prdOaC7|fLBW+q?U`XzAFBF)Xn4It)( z@?{#m#x$(Ylq^S|+S$~MSgg|<(22D}`Xj6S(fZJ;Rgva~sQ)Oxi(aMq;@2MydYk?v z7Hew@w_@wg8R!-RD8e~jrFRW_kKTvkJ0e!+V70}gjZPmhdHa^H@5@iXfKDG_t-jti z2n&S$R>C7#)aetZ(!LRio^?$ft0SEq{!nXc1gQalkn+r+E}j0&R5MA7bJn^bawI1MyEW^NT?<6G6LQh#NRlqkkn-)&WBvJw06C)DmhwCeq## z>d@)mP({(0iZNrxxadovBBpN)`c_y-vPfJI;zKQAxLj1DRAFC-42p?Z<$wbq1w<3G z!HQ^dp($>1qFFWAE}Atoi>wf|SumlSE<+$`@7{3j-!`a6*O}J;N?PLkkkI79vErd+dNzhob{gue#xZB2-P!#vpj{fQv8YUunEKk@)qo9yRz9 zz7&dB)g+P$N7-_5LKLC+*9KoMLNP3Q9Y`THu##QA3a;KKYwt;XMtggt-Njc@u`mzp zy)YkpXEf9p*7+Kyaf?N@#xG|26S*=r^tPkKDWR$HBsk7Vc&zAH-{D^#k!fsnh0fcU zmiw)LVldbY#~%|#NKo<1j`)E?qzd(s=FXPZC`v(nWN+q%t-itEmu={LJ=4tI1^VBo z95rw8T-h-fTWd>ai$9FBEOM*0VSfTwkvJtXkLBaWfZZKTr^Kn%4?2;@=`C7~`QcTe z&gPCG>A=VRefzDytbR4_e8OU-KCa9C%PWxtV7(ie=Ec_&2|({zUoIvEh+hHt<W||YHi8yBE;vy>b0^;}2lQ%1ZslK3vRM_7E z;bzPo6>4sbv?CL1@h`6uT;9^hr3F{t<`E}42M6>xl>L*jRk*bRU7QuAk}akBA(spt zO-SZFWy|}yX(BaSqVeqr>Am@oJo~~rZ=+(J?_$DD%}TyU?4tMn@cA)LROkBbw_z1S{3au9K$oqaxa4s`RL+&cB0W4hBZ&$8oY6 zpa8M-lLkM1!4S?I z)kCD*wTG`YREoy$^7|UUmxy+9tC$|?#I>Kn|H~gB=aJVKR@3*WPo}sZ88nF|>--6X zsFFf_X3#_-#9z=ds#1vcH-o0pbO;b?Z^V9_F@!l{Y#Z``v^j(95&J_5vi$Z?Z@bv} z>#;4n7DhVT>%&$Kmy`&X6+)TFacWyz!|n1~R9x!0`&~Ft0W@qwn(V?4E-hukj4Ml7 z;&x7!xVV%hZYyPpyGmK&rV^zh?kQ!7TS}r-ueeafv$);MBop*Kc$$b({7Xyc9vZfV zG_{*l+kKRK4;pQhkKYbCB!~Q{EBGnEkDV0bXm}B>L=u#K86P+yu#1WUsZ~2^cp%O8 z$xbQ_s5_}VkVzwU(x`yCS6gaBy>2Is4y0!8q{;yAq_N`HII*T8*9=I(b4aB;)berB zS_pQFDVItpKxH(BKc;c~3DuCrmmztaK;!91(16jQ#}aT7CX+@1; z3nt}kqg6hwhtA2_PAhy`j*6892fL{~m}DkxrKvvEOxjJGB&qcHgH1MUJTCs&I|O8- zJ#?XZa=gEr45*`<9Qf@s_R!{~0guyks|}#Ji!SM*Umq8Ms%Dz*-E<`Y>IK86J$2|y zC{^IoL)R#~Y5S-iy7>Y6E#5#l;?mS;iHdG@-oaGk>ZV~n*8_A96U3--`gEUDtRvcX zF>Q%`XbYmThaRkPck@=CyLYH-l+V>e7BW7~XUJLjeNHe*4JLO}mCrDfs&15rad+Y^UFj)A5ZAJswXcy>R(fN;aM6dOWUh==`8l0#acnZ}NC9 zh*W!hsg`atFYtOj>7LB+1#9YmrKq;JjwKkFd->MbeqU<;wSIkER~nmXg3%^{__t2|L(_i03Z<;WMw|eLufShRlUo;C7I;`VEF&?TPH2!1J3}1;i z``|>MZliip!n}k9xw~mruUQCl4!VSqmYXhN*D%xa#M++;TCkb7(*M#!e}fmeq}`v@ zL;tXtOh^&CcMpBJH2texbfCs%3%a|tPqANoEvVX`pzo9#!@gImNlLCxPN{~T(z~^M zpJt{@jjX82@MQ=q%rrCAlZB{qhG`3Wx+&9V+Nw-XifG;xsq6Bv=@Iqqu}_`jJz@gr zdVOA7N}Vm}>*iu#hUv2uU6bX@g5i$O7RA9}E(-Woo+xa={FG|H>DxxB0Z+Ea6F&dk zUF@j#r+EF1)=Jfd$<@WSU`aP+c#D0e>8mnJD(kC@yGqzBu^9NwTj(ubQ(a;UmUYtz zF-ASP$}IDic>UIdvdT5}0W;SuGxN;+UF=#@*Hy-5nKk<*Ngp#!i@uyISLhq_Ns6~P zQe9>X1~4qKK;VQ*Zy87h*p#7Vd+ZaRSFG(VtMjG`j#9m;nVkHaYmgiydsBCFYQ2Qr zlRe(k2%3?Fi;`479*dThJ2C@JWv2$}xwl7;C zn9-&+ zXS3X*@@!XD%*?tbRxPF7JXoHCK_L$W$@OL5&zZqISV=c6H*>bqLNmwAzn;qB*?CpA z6DkwS=2t>1lH>f!HD>OnNPS&5mB>-*e;n17&t|^G)eZ?ZbP_vN^7YbSzJRm9^S&X$ zPOdJn1&aiDy;u|hyn?PGHjCom_2FKW;w?(xZh^iij+y|iGyz(5nJ)=adW(c~LK|xm z0;3A!tFQAVK|bg!(eEv=+WYD3R!O&F4pZItN-H2>)WHghaVgv*N|JP#P<#BDLxmn&>BPZS4(eiTq)fhcUj!j$SF6Bz)!Jg?Ww z2ZNvr|3z3ZbKt#|R#{qk)i%=1vS4XHmwg8U%TYif?v<;7k!%W}X@s}TI|B1Yh$dmi z0dHBr^qT>*z$}!;=*y0Gn5FZ(sjP3@i0>O0nBULQuSl*A*n%UG0C)q5xg#rgv$s0X zHImJd7D^wbcmt8@k+xukh#GNLDxj{Bh#D1aR>aN1lL!3>a78@0$IRU8s>!hhbJbv; z1qVVwo`3^ot_4S4Ft;m@%{+_1J0^Ujn|!_;Gf!YlgTWHBWGkKJ%k$~+4QM6yw&Q$xsm>nuqx2N_ za8bW!)32V4EwFz}$>0{B%UjUHC2>M7i%<^-(n?deQ|7%q%9kfLOdjLQv-R*;>HY%t zf1sTM+bFr;kgjw#(@n?r1Ml{Q@A!tC!9ncE-sB!W(%OH;R!SxDUZ`cXnGSoHfI%62 z6u7Y#l@!s76T@p5i`u9jo-F4UTays7(yP=;S=g*Xt*pm>D|oSHz7c@aS0XUrNA%&~ z(dA~#t#3S#c@ItqKAyod@#b3^cbA(fABb`YF-?UCbGH#~+}}csyOfN8?L~8Mq;xg;(|S>1tj`*Yb&U9XHbT+(z5^0=y5p4COU+ zBVR{1;e>iKKT6&FG~L4ck$=BJxADL5-uxilp)mbcans#OGTo!3(Y?wrx?jnn`;*x{XZhBOCgnp+yPy3Wt=yBy;dP4b#o>V@k zXKcwxqO<6ETYw(29YMdhRnvak6uQYan_jZ5q(9hBr&ny3(QCF_>2=#Z^oDIey=nUs zy`^gOhMGq2s5$hWT1fA!CG>$>Ngt^}`dF=|Pt=L@sXCQDQ|HlN)W!5ywVpm#o9OTA zI+W+oKh^W-3w1Mnss5V2Qm>|et2faB^>+GNy^p?8AEa;9$LOH?I2}@-p_uwAv-&>U z)DKxz|IBvv0IPPD9d``IWj=5+g& zoN2!nN7kF!W51h++3({l`!4#*{ut-k_jA7e753Zzgz_^kw12@x_81pC96a2S#3hbQ zE_GycnIn(O9Yq{)9KjF^qJhMFX1-*1U^+i1?5_f>KnLIKawU6={%FKa$bV+8ot`OgSR+u;jPYHDEINT&ZoH3`66HM ze2sTF-{c#d@A8e#FZm|t0lvYda<|LPx41I-R#zVCrF^?<4Bz1j@_nwO_ie#EtxA9X#(kGWpt-?`r4eXjTTao4B( zgzF1_()BGr<+k(F?j(N3oyE_(3;8+sD1P33B){OE#CN!7@=NXo{0H}P`qUlf*W3|) z-Mxw5a9_-Cy6-^w0Ketl$A5G`!*9DkM)?K*$*|LVMjF2>ED8xY8`{i54;HHpP{Cb&;2#xn79ZDa5mQ>k~r|$XFAX3Ik*<7rUoY=@KzPPsGA)x za0@7V)hl^~>y&jgYpp3zLJ+(`G{*@@TuU% zufC5jblOoXR6nGbIEq@a`U$>J68M$a-H81iXe+TN(-E?*%s!Ly;pdXrf%V-0>)OK%w6( z$9~!vU-wn&l&j@9-lkT$TCU>*YLu(xIXaaxmqJVYR%PJ=>dyp?KFIF zCVAG*#ttfZ*3QRHEqT^1#BM8)EYLQ``76?PQL~hzNZUszOF4#XPtppxTB-I9>}s7r zQlDwd;Pt>SOJ8EM^9CM9+4|DB9pva6d9maw2hnn( z2^Qwvqg@ccgO%4d;z$8|l0$E^h^*XczyS&!8N{(zE3nxz4xaYvj3fc$7dl z4^eakpUG#THkM7MSa00uEc zu!mh?gmRG5nUxYHkQu`(kn|s%BN)NNLo`EGReRt7>FF_G^B=kSmd^N=j)_4zuA%7@ z9i&u^zT}vAM>6yn(XG*U8voK}0|PO* zq5BvO|FK3m5I+$4mKMa22xk2>PKdrHM~o_e!hCR|u?HNaLXL^#|IrM@2_&q+a!ux8 z&`CBG|NoM+v2d^NXrk>KDhA0k8vcJkQqV-O&!m$73t}<=#PB-($7`<_ec^*SKam-Y zFA#1pOi3|?Og}QH3Kpad2r?QzU=2AIV<=CgFKz86$ z1o5^=d}<)(^*)ZXP3)${Vn%i)e$P}JHbrV}LA4tnCSZDYm8q7cnCd3n99~w3mT{s* z$b#{Wy&YXDo2oVb;q9?!x|p-^gln{0``)=vf&~%(1=#-;$^YM|kUz(<@gG#l|D>b& z3!2Yg(IWmg{-Edp{*vTtI-S3v%Td1p?RWAa3Dgl51aYA7Ne5qn$jEdoQXG-aF%F$a z<08uq=vL8DGG$an0tUx&1d6}IUmzaD&dQ^{#%q6t@!hwDF8-oHoMMUQXJ141l)LVw z+%1%vtL`B89=;Mcv%h&HSBPh-RS)l=oa~(IC@KD&TPZX$4n3llouis6uCP<^jsu7Z zbz5H?6heLCuOU@MA%}vav*MuPiberNrz7w;5|fmqeiF{_HHG;yb!EXCUkxV3w;c0j z`Yx<$zLeUM&_SvrJqx*#_*5${)sX0 z>H9vzk|Uj*N(K(ZnUt-V2wWZ-g)6_H;-ks9_Va@?!KagN<=!dIs-X*uzg8hkIkO zPzFDPbw?qA9wRR z;t9*epL+O1(Z4T)KTb^eR5a{iJfeIoga4f9_-mg_90Ri?C7*Pq0B46n+%*)@NR;E0 z5}K-%(hQ}HYLx)3Q%2EdWi(x>jG=C&l6EUq^tv*ZK2^rCq6GU42Vw1V7Jp};)3^K& zI2OLL!e3i_fx8j~7GEkI!bVq83xA1Eg=}%xP!SO|zD=A=a=)Q;SQ##Mg}HtOe-s~K MG5!XF;fu=u1FqBFNB{r; literal 15036 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 diff --git a/software/LepAnchor/CoverageAnalyser$Gaussian.class b/software/LepAnchor/CoverageAnalyser$Gaussian.class index b96bf650ac8d01a6d3c42a3101a519a80f653dd2..6b4fa1373ec0f76c55847831215ee97db8b50885 100644 GIT binary patch delta 75 zcmaFB`G9i+53^(@11CckgD^ujgFHhHgBC+BgFZtZgDFG)WL4$>#-hn(%r=bWlQ%O5 euvRnhFw{)uU`b$Y0J0h<=duJaCQjbRq67eaQW1p! delta 75 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 literal 9179 zcma)C3w)eamH(glzIlJiB$-Y+>5HMQv`LeOw30GS(`QN>NZOLLrDqE=7#pe z(M;qTL1FW%ZPzz$UfZ&2bK}Mhtpa;TvM)i>w)U$9`kqvDpXzh5XHP8Axxpk!YTE_Q zSbAS7vxz%9%-$kyu&g5RF>vQ zX0L%+xP;h>Ae;FO2VHEm&^Aha%NmX`mP-lq9n^MoW}5O*Ckoz#UIUI&;UoR@SV#u0K@;^K-Pae1bE9!|EGMZe-|oZ)L8-Yx zELpv}Z_l1+D%#l+jdUu{+9W8;!x|I4eHoIDMtUr;8!g!E1pQ*{-iOkeXpey{poMKQ zx?jLzpVY@tsPkYWwkfT*34%EZC!myX^`IH;3TcPHKZ;~+PbE0eS7wGzi(zNLiEy)9dRinF%*G1h$j9^XmWn%H_ zRjE|uP*W_Op>Ftds zIt5i@y(;ewlVr@1r%-8pz`#wonddl@wDuNEnO{C>32{G&4?FRpyaH&|F>)B_2Lpz) zSBVx+C~e(p;0QiS>ARv7F)+VzEYTcm9vlVZ-^T^Tqbg`jFrY&hFIH+|B;KcG}avUoBb_23xpbmG%_R!zmOJ=nph7)vG$d`6-AOjcVnk&f=o zsYplEg}WJ})}~U)R4A4R?V;D1dcH@g__Grm7T25CKo9inSQdZL+V%Cyw5veZ8bX7DbV#QLaV zrsIr(C-D?dO)8S;O!k-uwn1G~UpMd#bt!4lgS{S{!nYJF&%6sO4JweG!gmaOSFLe& zMic2+=8zKM`)cl=Et8Y*Dg3~|5Ah>9bEKmqnoiHXTrg|0cq1CBD>vEsv4MXv*@-8+ zT=-Yon4PA2bvZn5;NS5d+;x8>-WT1tM;(rfNL}Z`PkEoX6%Q{O__>nW#q-#j>5T4I z62D~Nm-rPW?olz_KEJV19jNK}wSnK@w`x9eP|Y`(*K-U0%fRovTe)1wJlCVHzHs3}NA`6qKk-1M)WBEpRhI-w z-(BI7Y4q!@$|=;DkQvmz%v9RHh<);B!X@gQ(I(8-w`h4!S9J)v}@CzDLZ)1j(}Mb{-u1Qj{tad~+C zhAqu%KC?F(>WwF3orgm0i*~vsENIN*kE$k*ZQ+mAe5gH<%!DXm&S+7M^q5K1QEhe8MT5@PLd zVaL)Onhb>6Yc9Y1a_(6#SdrV)szdUdi?)RoAI*ChJCRUrZt^1ob)m z!!+sy5}}Y8+dG+crDD5zB_$K1-cLprUa`}3Nfoe4=eJLa^eTarHM}A=`$UA`xjeWP@x}ryqo8L zRoaTef-5h=;YE0!JVhat%2eQ8tOV*ZWFhW%i3)*>Jkl*aPElcCGS7S~U&I|4t9luHi<>6+1CNg{dN7lUjLegO+w@gca&TEkgu2fi2o8G~_B^f0 zzEnrlLYS1pEL4hM@zIz_L{n?xk#t%W<*t0YIY#Fs3EH zh7h~6t?t}M39{ibetOsftz>L7=9#k~36{_~Lbs7^9v{?I457Tjei{qZM^(iXRG&so zMX~nEX)NaGiqlw9QPg=F;R-oTY{!$%)2Oe|PGiOBW(&CuD7UWaYjRx3-vFhWL7C=I zo^p;BlK(3x-BRKyh74QR!?i=WZV20(2jI6RceX7U!u4vZvLrH$jty1cgJS?&s|FDxu>}ue#$!mH zLAtG^ZwLoCeQ*#r4&s)9>`$r`!XQ5K4Xhu=$7=O}uC_l3uWIyNwQ5)w(05dOBAW)X z4^$4~jseVWX8Wl;&AUjGB#v7&ZNs?tQOacC1WvM!(M`i_rwM>s&R$6^g{hULsKhe< zuAmN9VjVSdHFdIuqi*UXfvZ^6tz!YU9(SM-_YnFyJ{+7t6TZND=}Xvvud`Ht2G^Q; zU2b~Uw{v=>7V&xF@)jot58y$%#Tr~ke!f5lxQP~ei1<23voL@!68fc_Z7i%W6YC#2 zQx5kzR65b@JBI=%ZkTfpA$c1fTb9dhPKcB2@#EMml3)^JnbNbGk1GMdu2FYYPvKPa z89dUa%(_G=bqJ4bn0|cJB&Vr91slYWr9rJ$AHY&h)dX~X*O6}as*WVX_R4^M2B+F8 zf_4R?@0%y!iEdxlFrMxn!Z%y{busN%bt3yrVOZB{=>^k=@kH3k74-o}(Ag5ygU*hy z-tUlrLlHii{f#fEC&Lb{)-`|<6VJ7yCg|vH>32!MW#PQ48hzK3&ZT-`*sj$&2W+!O zrc4}y+-ioc$3*cC;Q7L^q16@+U|nI@7bsNV2QaNNP`D`IJu*m7S4Lq0Mt`AL1WslLeL*7`F4Ss^2GB~ZqMBf#(otQ&TYa=UTwLY~ z6j6ZUj+Q`?wPgjjs|*w^8pg9-VSkz1f(QNG{MXS^=E>vt7m2mxWcJHJd(arhb0c&+ z!uEcz1k7V`GFx64_G+~S11Ji5xnDs|N7&n6Ab|q4;mPbDeL-(BT%grbumGp(13qH; zYJvsSs!sww3+)xv=$#5{H@WiFkQJZaS<|fsY}K%)`|f+O%IxK!9pNGi*+S|FmncB6 zND0DKszXSy#B4Y&;g(rK9?0HjGPHbTtC}R8C|ndQ@^KG`FI-d^@R=S)zG}E)#P@&XIUw^S!DEh9G*&kAE_dJIGR3Sa!9>(v-s7xt&5PvXr zR_Z*;4fB?vSNf9S;@rBD7r4k`jE7|uNvrh_pfccB?A(vgcz_iTA+o@eP*V-^AY;_s`-xyy3qm8on=X zzF`=6Rs#5eRPy;^8J?3h__16~*jBzr?7;J~n^|-pUm|YC3-VDuHr$Dy$rtdVJWSXb zyd=+&-iv%;cnL4dZ}DsSBipz6VvxljY<9eAo5D7PKiL-GHCrQIw{63pZC&__ErGw< z4&V*jO?cCGgz!(|E!$`Cw(TLDwS5)u*dD=o+jE?IQ8e2xMYp|)r!^6Wri)Yah)XMF zJ6k;3W#ZLhQlKTpryY_a?Pe*~ZfARsOwk^YfcCJIYG-7s_9F>uKbL9RS(&c8WQJZU zm*`~@(r3t}`fQo4Unz6+6*5=9hT}H5Oy4Q<^gEdA%Cvh@s;Jy^2yt&WN^HlN%~*I3VZJyOl(x=_i(cx~oS1I%?AGG)>0IxSyk9&@BJIZ9 z9$s3!MO3!w)z0!ODTBQNy^POdO1lO64E&U@UX)G0g!z@)tysU35-52~^gD6A6tGvU z4`GQ3^{XsLj-l#2YMoN(#3Scmm)a~7SO>EkvF0@Db!wJhVL8Xlsma1D?KNi7Nf+gG zmUj1!qMEb(u>KK#9xhGJVYcQwiy}pr8|U|$fUGHc@1V){7G^1~jCaFSc!r|IH0|QF zOj5=EchGM0ojNnVldV&o^=SLz3^|Ui9LLL{v2+sw}kfN!i0=;AHR|VoY9f9ADvu>6c>W?^DFT zUSF=Od~t6@KtG~t7;Q)bVSCU%@2>k$le=02`j|VyysG^&btGdC+Sdp4<;?2e72CRG z&FnIu?;;nxx<6GhQ|*^q0Sx?g6EP_EF)%r;dqnRftlc-DHtT8NRjNa`^~ zmb3OUHZroEVW6EsW{1f@DLsrdMi`hu29)AN_&k!^ zX*qEemP2V^!6;3$#%9R0%dRoB93!+GIblS-gyd+g%hB>GT8k_6admW|5=c5ouoE7M zjv?wDA?h6=$`_Oo1~pR^4yB4E6B*n~da*II#xtmdbqgQ2aJ=auFQ9q<~z&so#pIkX7!tYj<}3Ug(SFBlHaWNQvLfdS5l~wG-@S-6|x^|VP4~-|);7Ce zwzjr8{Bm7uo6|4bTH9QHX=@#n_Ujf9O$Oz9^L>Ft&(O7oq+_X0z;%A*jbd> zb&mFwDaaPT?6n|tO^&xJ5=$Lrj_;zpOfMsMHw;QI0ozP~v&{Kj%qGA|T7!~OdAuUC z%`cfenUp!x=9m3yMy)?srqlBem+6CY#~HZ;Ps=CqZMHv>Pcc(GhF{31 z@jLkpUYEP@rreFJ92c8>miPI+;$=HU?&Gojyj02wsgVaw8}BgvVUgU#Y!l?FLT(`! zd@CX5hxlG$<9o$CGXiMLeeWlCC+Oi1;tuKO+lbEm_ZE7EdMA1T?d10a`>!*Txs^Sq z88haV65 zr%qL!`{m^w3mng$dF&W~q4e~AhsUlEJmwg~?YlgCj*bTilYxv{COwX(Ij*1vqD1AB4MKn@O>x7gFv z5FyF*xsoz<^bq2!{LPK^jdi8ImSy!#O$_-Q=)!#YbHu>!aMb+NK2q<(?+qNs<7S>d zz*mkZ4g5hG=gB9x);9ZF1I-3L$Nv!2#@ZFEE8(mz%huj~IPnR-Fz_V?5bR6*Efq}aKp8%C zU=99e;O}T4==F`QbNmf$wL1Pm$ZA@$vZcPp-{5NswA7o;jx_q#yu;DQ)rIe+lTQ&+ zvnFSo8LmEaqZZXRwJw=Z-%v|%)C86{HP^PZ)GlJ1n(AwovE-H-|Ki1g28n5?Z(%F7 zmR5f=k2Hl*EvIN>nh^}?Qe$gSXfiw8rG6kUY=cUCl4d$%`10MGNstbONc%1#WBsz{*^5T^`TTY zWpRB2d%~7Aj~VAkCFurbkdMr7oc#vH(jbE}DT@U+1nLGCF?|-xmvNWJKa|UUp*(Yx zF~rqH1#(g-oit85$?K35gC%l^x!vfiJLn>VhEcKktT8Zqgh3@}W-9}2tvZe5Lt+Kf zd}T7QsZ27Bl9I|TNs4WBhj~%-F7ut}wYlulfCNzAyKKDpy$}Ske?s#??Fwe>MGGXX`NRMp+4sj@^>Qb z0G1y?S9N@MHx5huV<8+1p{sky@UJdee9+W z@gRMThbWA_q7NPx19&XJBVs%r6|=BkRO5j76%LBu;E?z&y2LF!@8I=&aaeTYF=Vk% z-i3#+d$0%7QNdHb`S=0%NCz@>8hzMhg5KbLZ-s&}v=Mw~*s0~zfgjnG8hhse&M=3a z{jvsOUgfR|vh))79Vf7a1Vc{tR26&0hG@KvN>Ul;VsEUA@CJGBQ%Pf_&buCH5juG9 zNtQq-?^wrn?0~}ijIvl1-sOy5XcoqjjTLLW%3+fu#>X*a z8A>|qvNSLhbh5)(2!k53nc-0+pN7wlxiR{Wh_<6l8F(56cC?2v28JE1rJeuh$`uBA zP*qZagghbX^}J@{Q&!`AE)!fN61m_CZn5N)A)*v<@?FWdqRHiJ>E54 zDONj?pJ$5H0sUfTtx+jaCd|U^;g^}ly4T%uF8`mRL~~MV1|^wGQu8RyT%S5Kqi>0- z6x%x0Z4u%1>Y`$qywB0AX#-QNn;+wi;hV2D6vrmUsteAF8<#efzB1RPJ)hbC?k$K- zj!Jg+(7>9KsF)JxnnX%WOpM(gj`}Ib>huxJQQ`BMZ>O(F`RYc_kip?e-k3yBqGy4+ z;9UAJmuFnIsH?i0vP(0(8D3Y2a;m%;u~8w)=h2fCqN1P76TO@}emYOTaGsl-!geL6 z@=slehDu=vXt+0n(~UXEmuXpaGo3Ww^tJbw=J#eMHR-ndv zkzHIzy10>|#5!__yYPkBLeXLmxx}LsBM$TU6vc@bDPDX)Zt*E4h`*9Y{6JoXC`IW* zeU!eGsuc4$mT?snr_7-NN(~KE>S>U&iZYcB%2GB`wz8RWl&zGj?4&$}qadgpqXOj# zDpXETk@74JR$iwe%3DnE5nZHwLBo`9s8~5e!&MuNP+e4_dZ<)Qr;+L)x>zlsGIbn{ zQY&bnI+w<%^JuKiMZzgwY`)CY}<%Hnl61NS>oXPuhjLD>^A}@%(G|o!k0c{GE zlZTgA!%5?rP~lRy+#*z%@d}O+C$sE8F-?@Yl>;w^i*tlOZP`z;m45 zxn9r&WglJ26~;xg6>eKO(Z|qyK2w?KV`-UkGtFWtanz!;!(j8{saTAcG!`;wLTFB+;vwVcDO|5naYN!U-?ObunGW0{YMg=xd2d<(O`? z{E;j&k#2FMh^d%JbC@GVRP^Ff#U3$*s@SYV@i3AtI*)jcn*nKpM|=Q|e_0Ka#1GtW z$kRy{XE4(u^eTOj$p^s#m2@rv(*9IsG5!`|R#x%dp37w*N$J32mK3kD5#8KVNRG`o zM3*y^s%+)2BJ-FxUAYhAEy)?&VfW|0!wFwd`2q`QK3$0n^-64rh*sOs7BNe`9alum zQtw1{#4L3miXvvI`;i|pOLL>vf}%AqmvtE_E^RRE^ecv9wMqPiK*mR`HkIQ|Mo+v| zkNL|8MNX45RE(^+;uVYwb1$OGddE+4s|3n!!m4uQuwxgaxb~gqeV;;Hm@C}RX#2q) zVYYMIMJy$ei)fg&_tG(f`G172P}Ed2In$W$kSK*eHzE2vIKxPY6mHfg@r;%g*m+JU zqoh=eAwq)i6dnNU(oz&!J4 z&gdX_5pKGcA3zpx-C5u_4D4qjVcA!}@ePrKD3ObJk%vAa9|QOmkS_{REQ&B%48|le z1ec1TxLjOh*(#Mp(JHR-)}vnx(YP*{@R#xk7y1TMEmQkB3L$q1;R8pUt?qzj(!H6E-Z7y@V>An!{ z=)n!6j)2(6-KDBmO;ZCN9BPF$UwsSWFh>_>GvzukXp&DW>p?Z7RRcrs0^Fj%T=jyeekk zbuklfiQp`JE-LY(n9ZHg9PWCmC|}H_A^gaZ0XItqCJm*dB)^>NluN&-<9wj2_&`E%U!uY|iJ`J>QqSd+xbsyXT&J z&bhUG%6B(;4xN8_AAsKSN~g!w!w)P%n8nH$2J2m zV!OG(nU(iEb{HtYPC-i7(KCa01&eFyim#tt-_%&qIHx)|>*{V3oOsDR;7pe`^CM@n ze;01?U>9CCum`VDadiGR7kvZ6rU>=K$GoLa%u3gw?;CI+>4s&%*KZw_ycvTQ% z%dZX2o?bkq%u())>{1*y@O#VLsH(czjlr7P4Wp{>_MjAhFz^ok zXg=r~=Khm`5dK4ucR^iL+?d;ER#!D%?ZIOFr-47?J+t1O@A$yLhd5@gb7vKNgqco! zEQq$%H#OGO7FSd^x*_<~fE$h|e1y*o`~{zzU%2zKPvA=@{#y`#nf&_3>N&y2x;X~E z!q;ZNlU;lQ-x&B7odwPr!G`kcd5s3oR)CycRvl}M_Yn!TF_`$r#QzSo` z&w0}5nNybQ{>MD*DGF%yHC1XYp7|gshiCs94Sz%>h2O~wKWZm z=CG*pkVBAc+to;w#ii9%HM4@X!|UeE3O2gL#pB5B(Y;5H9v;zoOcEv0PBDJw__AQ* z3`1hXOWE~xa|L?W($a3FPKhI8^5jyF_$1LO34++m`@eS1oZ#IJh9sp+GOd|bQ_KB1 zx=tqN1WSwGkW@)CyBoQAQIcawC&{JY+PdjIdyu><W;79VfdC52D8ogY*q?5*js*XX6%~{c#6{$D9F%QZVb5%@6^lg>_B2|XmBDb2a#^gzj`BltFujT)` zdduYvB`%p|4)qR_I`cm7HFBqU&^t^TOjqo1X)?#~JJ(zh+gt8755<<7_r!H7yjS2I z8=MoIRm}(?&`>?cBMT64$wC2m17#6IQtrDPuL<7!d9vB0k|l;bAP<@yak&K#88RIi zE_s-4n_6ApIKzc_7jzd47d$RRTja|Pd0bYQW8%ByJt2t8wT>ryXx&{5&FR%6%F0}_ znv#24q-zaXCr_HI<0p>aU|UxUe7kdOVlTDlL|&;(K>Z9lq5%0o z$_nG33FFySNZEr)m3#1f8la_U-j!>xgv0a!T9F#Yz5CHz;waK0P&*Fvb0#?x z@aFO-;ib6vwjAax3o<#QrFc2SXM^$;zn;nB&V3Y;Ic-+!dU@?dB{%-u2;3a%ga6#@% z7<_5vxS+fxgruU~_(H%flyPY=B`ljr)L#XwJ5E@bFup5q!OcG-yN+bGU7DY}uIa1m zeH|_6>DLk+`c!*|i^Di+O)pJ0M||-;eWB9A#SSIWk*Jq!=s0pw+lGs-|B@^G33#n|L>bCCv!K&h z!~G~YPl|OM`{6AxSfFw_O&B7~6VPbt)uNDDmk|16Js}5u0c2Bc#DF+LwqSbN!~KzykcMug zszcmaWIHj*dk%&Z1J&GfxHjZOvvw8%Ifc#+%P_?WAYZnksoB-l|9ms_< zf+87_ltlATpjg~LBj&}|kU=-`Qb(`h9y{KrPl--|;#W_!;p1hyeJk&4Q?@dffOq!xi zxfJf^r#UxH-BsBpolCO(S^k)?beZVSigkyjE61eduoPY~PxLd^ubgN8d|sH6O7$t} z{7er^cT3m~>E+Mj^)Od=%D1h_Hjj6zkx$Igxy`xRRRi1?Zj(>?@H(YXalgoAO3_l> z2~uL7%*_w2XQXXp0Bk}YHY30^+ZWH^8niH0H=&Yix8r#p{Z?L&ZFrdDN^ED$w(=tF zAoWh{WodnlDdsTe@1c$1)6QOIH@@R|2HVBKHqC{-5{-T0!|x;$`z3$_QjCMrE5yF6 zKVFmJcwNThO}PdCE_dK9X}}?whr`m0-^&slkw-bM#XGVQe-tJj*^58P0lX`JV0C*J z@5y_3Up~eM@;N?~f8YZ}#W5uje^zqviP9OLDg`*M4B$8npDCm8xpD)(P^NGU)#8NG zgfEo^_)57SUn`H`ugYV1M_G&Slr1=^Y{%b}-5g)X_sS9cpd81K%9r>_`39$yGdQCP z&Z=oRr{>_i+Lz-H;>z*8Izb$2l{nQJaj6T%tu7ajx>BOl)ncgYBwF1hF=|-6A$6a` zss|-beM92aL*i54l?3&D5_~R6>bH`t{v;{tdGTuwN!4N`O-qt=EmJbIJjv9$OO`fN zvbAz?YLg^an=GBRpmfoiBu`r)`PxG1sx6gn+Hxt-wn?GJCPzEU@gw5CBJB?-dsT<@ z)T5=B9xGSrNzzC6OJBXG^waxu93uVo5i&q8mlAyn$6C_NlOg&d8LF?AQvDejravp! z=`UcP-Y#F8Q}UA4U&%moeqLJW8b+3mI(?)Jk~n6#MTnBYNMV9m#guI&AC)n5GgFq8 z>o%gV3?a_Jc=F0n;+%}63@Ih#z^6#HSJ`7~tuhR0P$gP>fnh^liPdO>#Y>kgl@WxT z5(shk*Hf`einV*0wWHvcUfO&aMJP)8sBbenIuI?x)on7GNj65ttLyDN>6Ke}R92ph zl{?f2rHoSIq(N_b)bwTv!CJGS8m{HnjA-tEhe3zI@|U~ijt2^nA%e*a(F)sy6y-d|+Jt_FYd#Nx0@X}(jwn{A;qMV< zbs=xtc$S)Ebva(Pcj8x9qD^kJIaagVoj@pEU58egNFAB#dJMBAXR#5=l}TL9R=-7P zid4!?$kJ|QU9|1enplplB%iD;#my13v}LG_n5At)kBC`XD~cj!={{83P_*txh3%Ob zy(dq1GL^*YBl!NY;v-fcg&bQ-yj{)=7LM^D>oi%ryjJZ}UPEaIk|O1WZxg!~b#P6$ zDv|KJnJ8AFv)uP-#C5RP*rn()ZKp`pBCW(yzV!u*8RPGQ2#F>CIk;4Dk5YXdlaO!) zhp5aHh(3j=b4XC7gN~27%%|G&Y?2Ok2=TwLV%N%nmRj@2ZUrIB3c18~d665ju+|sW zHWHBhPla8kzrfJ_UuchrE(EJ}Dt!M^wq@^I1x1zr%wB6Nn&lwd|J~^SC`xbqe@Y)y zSonXu!Q(#7n{k$HP6vF5`voIa_F$HmfY=ViBj%S#EQciUz)qvT6|{~kX~6!?#dhmv zyFD>a6&5++pTJO7?0=fEaEdD`rQ6M*TyEkJ=UAuT*WE-U-pWja8hn!*d zVj7b3@GHbAf_z0mcSS=V_EA>n$o`4n(uj)Mr4Yq7tALM2DEwJMiAKB zG_w_9;$;6)kkN}@b|ht?#j0c!?Ay@salyV-h$;+( zib~BVODigMpFC1g>F~*;6_rk(EUTz=`Q)*Ru$Wd;WNGrrN-CltO98i8NDiN@3d@>y z+;`0$2#tHHgm0G&ZE83q>$(10;cAIaXsHc3{Q4Vk`5kXwvc#`(@ll^_w8cCWmd!S1 z*%g>)Y|LXmdG;dB7Qe;^p1i<856d>bmSnr-??@O-gP%RTk_wlS4nxU699{_>4LdRS1e^4vsNj@(@Fqal_In$-ElxE#@k8{98r4WsL~sMQLbWl(nnlM zKk+L4B}o||X-bKGSmW%NOP3w8lLol>Bg-y%i3c~=;0+A31}cbQ6hun%TE2^PJFK3@ zTs~;Z8HjDDmS2%h#{o>1c0vw2=mKz@=RK1JOhl4#8u5GyG&B3j?h82U?pV!!Gsq$G KvK2Y9=YIiI!CSuo diff --git a/software/LepAnchor/FindContigErrors.class b/software/LepAnchor/FindContigErrors.class index a6b87123265557ce22a4e50123694885236c746d..09aff0fa1a5c89c45864f3d8926e74a25a8a04c1 100644 GIT binary patch delta 124 zcmZ1!*B`fmLy|FIGpFPkCW&SSzP}6x>(;v`9DL!WC<-@ikltlRv^*oKMOk%4EjqL~H@7szq`X1V|sPc?r4 delta 185 zcmeyA^CxG+97)F3&2uGBF){CA;F+8yGoOuz=_i92(_~&*4aT*b&16eBnJpOfCa+a9 z5tCv1!@$az!_H_g!}N=R^B05BFQCfJU)8QMGsbT|t;NO69Ks+t`G&3v8yir87}F#@ zTgJVc?etQanZtn+%M5jyM42XEF|=mfx>>-emr+8Uf&DLo20KGKJA*6#9|m3^%L1fv f@)l!N#)i$ejBS{hof-HhOPXo0u|wSDYNiVS@Z2*Z diff --git a/software/LepAnchor/Map2Bed.class b/software/LepAnchor/Map2Bed.class index 4f12cd1d7fec842ca7ee56e49066322d63538c0b..0bf99a7da778b20c816747443a0dbc6a2f36a719 100644 GIT binary patch delta 29 lcmZ4Pw9IM48!<+U&2PmPvoTL$P?|hhHjt5P^D|jTW&o~l3T^-Z delta 30 mcmZ4HwA^XK8!<-9&2PmPvoTL)P@X(lHjs&(Y4cNAM`i%G4hnDp diff --git a/software/LepAnchor/Misc$ArrayIndexComparator.class b/software/LepAnchor/Misc$ArrayIndexComparator.class index 3226bdcd2cbef4e573ba2491fe3094ad2c0be3af..339c34575133df78eeaa3ecfa393fa52901e29a2 100644 GIT binary patch delta 63 zcmeC+>)_jPn}u^C0}I1s22O@4lb^8Ub4~;DW-tgd%$yv<+8{U=C_ayYk6{6WD3DfU QSTy-9Yb)dO$;E6_0VFCBn*aa+ delta 63 zcmeC+>)_jPn}xHFfrViL11H18$xm4FIVS^oQyGLArcI7vZ4jIV6rat&$1s;c6i6#F Q%%A+0wUu$nUH43pljP!lA0D zh1Uib?$SOLcbezoZW;5mKg3jNJ{D+pL!Gfcx3t_kPemOT>T4Hq?G$auV9^d4&O4=K zBqd;pMF&!?sYJhkk>sl|{s_JyNy0atZr%jWR5urC&l_xM^<$!NT%p{C75s`{gv zCh|`KF%%4?U`PeSQZS4KLslX1ybaSZU4e!dW$Y6u)z3@k4}5eW73s6#CA`dJM3PAX zXO2%a;R}!qc_$;`z6Kcw1n#(%>C&yySa&3;Vn3$n%_}y%iq{0p;r{+eyj#FqJt?|1 zn>I(fQVlgal)fqAyq^XO4hslHcXd>Eb?g=oh5Sr%nu7`s^qnVccmpF$dn%Eeq)OG< zLnob?W|~#Jg|}s#6e!OIc5}!!oWeT-a=)I17^QMfj-|k)jhcp&Jp=UdKSFYQS7O_; za4IaIwkEbFyCO}|80${aE}5o^_qD60Ix(h|m}A1C4Visnw|2@LN=xN1q8tz##xWx_ zidikS8O%NillycYSmhxyr#(Mf?ahw7P=F;S@Fw? zU-3H487%$~jgrrA^*Bwrfzju;EB+Ei8Jc?4Uc1CutEY3>4 zeFr8!8o?Tu-7k-z)}<8PnS2p$Gzq6EyRnGt$K)`FOZI4`mg#A?t7HsIGH7adyUOyG zOZJzz6n~k%^vJ{lw@WT8Q3}Rs-Vh@kY%}n(y%!cNM+urxhWk)~`+2ihpb9HF+l)nM z;p}Q!YuE;`mcJlc(T;Qw-FOgNXr{0Z+Y!PJbl_=p;#sW6UOa@u*nner7$>n2qr^VL zCVY-AT;a~Eh~PRN!EflnpXe1px0r&>;!ecG9K^+3B*bED5sgTSr3i@?NQqW#73+DD zleCA^uhuT4Bcnw9E(! zl@SFYqg}|n_CY8l_!olDV88ar(t9`Fb6H~Pt%00>;Zo?2t@tC$u9aNs=XeURzw2#$Zu}7O{ zZ7|Pnbh9kAq%~gi1!cQJQeU#>zv4UeDy6$d>At3*-;mk2%=IR_qHt&2=8M*lqy|Dp2c8IoOb+Y}WJ@fIJ20G`+07aCCj6-^ zzUM#qGg(NYM!vTidVakZs|Cjxy_Pzg*$TNoRKN-;`BHVUOhTFr#S zqO!65w|Gg6#+bN&_Rq3xS%PFFWPdD6_A6PY8~c$ZOZ>ycWPj{06BET#gqkILdAaAD zch0@{^S$T3Pip?~Xx}~gauq-mKKNXlC2@mZ$?qgob|&Lxbqxn#=d%%oDIBe`TIopr##E`prROy>xG zU*vgJB$>^%Mnt>hk#{qLn3F0s4UK3nY}z7brJ4%UfmgAoFg-alO0f9+N5oZWS+}Qn zZeK1l#i@eJsp-)#PFo_9xT*N$OH6#l?h*GC54|fMD=AXMxZR-3;<|k>FFl5g3V`}o zP(64X+arOxCDg_?qwe5F6kLIKkZ!}bj6h%ojRn7n{mrVL;1WV1*%Go^LN?V@R8tM= zwk7QS3i~aFSrX8#&jQ&novP_l&GL`&y>8QGF{@TmP<*IXRsCm1A@>nR+)7ZasxNwq z3s{X&Q=PYxZY|`?0^(m)_Z73Uv3PC$Yqz2$OvSCZU5nRHRqu8!qkRd7V)ddydm&%% zR<>ew%lV9Nfs9HXKShAY4%pF&QoP1ze-JO@5PUd{W*k8qj&e54UnA&3H+!$62fY}= z8yG_$CJ;p${WyaG%prz(9K!+zaTRe~=c-$I9Oq4r;TxR5w>XI(F^r#(z~4B9dlG*%IXf*w-v5_>9BKVn5ul(52o zSXY#{n_$owdoEUcpXEeR&MJXAUiTSFyoeGnqQoKX-eehKmg8LFL&-XymQz7>@g*X8 z;{kMUl_Vs`KJ^`L$9a@tMszu5)e9WuIhqqcIL2r}v^ZOBq5T2Ah2oqu?f;qkcZVtO z^3DE*d-y9nSVJ{_6~< ffp@Z{c$Tmj11mcNBTyQIfod5Tm^N<}zrq3lG~5wQ diff --git a/software/LepAnchor/PlaceAndOrientContigs$CalculateBest.class b/software/LepAnchor/PlaceAndOrientContigs$CalculateBest.class index c644c041924299bbb83e386164e07c3d5c3407a6..89601787de8ecc3b5c06aa4726b3cac724c912be 100644 GIT binary patch delta 790 zcmZWnOHUJF6g}T`TFRxAnzq!Ikcm>Iv^Jn93lfzD5kp*{3mV)Q(tz=SiK!yKfFMQ^ zQIt<4(C7kubP?rr347@>s;M2d)PXwcly7Ljcm+o4mWw$qc|NDic#W$bu6)Hx0ZcFg zq9l!jRenPf#eR`Q$gn4lPyQzey+(bZF($v|Y&*2bh?v^>L`4Ej+^<}rISuop0+q0rT>DJe~G9?>u~s;Y7!-k z1l1IJ;xKg^gkI@Oe>*q}y~<4E#l2B%)PWTG;;>e#ZhoU*Nnk7qh9o*_=?)rb0Aad|W*S5bMbSz_=%Qg6Ew6%_@hHMm3|;Q9e2`*0Q{@qn0o zm)n;65LxsH0eXyjT9S2ESC&3WU|gQEy2>W7iRXlPiNZ0Lean24KpK+?2G(sNouo7@ z^h_qL+htK+zFf0zaJ|Urx9~9V9oF>=%t&wER`<*QIh&b(?^zkn^A?@Ru=+{`bD47< N4>;u?7GD delta 719 zcmYjNU2IEX7=FI?v^#ry+OFBwnQA&T?VM^t77{LKLQF&!TquHFnPg(v!a76~OBlxR zZJKVg*^hPWCQd#1%KYbjy{5Eap9@yYMC7_dZ{q=Y5|y&tfPaqECO{z6a2R zX9bF?a+`X_Ii+D5PhaNM-y(him2?gsI*)4VL_KvOL>JLWm(WDr*hwkurXK90%Q!$+ zh;)(Lx;;BzF8y!QH z9xP%52V1Eh3A&42bPtDUAdTa6AFcENZ6wh_gXp0lT%||or3`YE#TOdJ zHyXhrjp92G`bpzL(1cKFQfM?Ks%To&&^Y2i29wDj-#EdLozIsq`!=XpT3Z$6;EqO#HBMYjVZJ zMCNE=4OC!@>5Na{B&*!4vta^jExq09b8~|0_-=`nFj-)t92uD;6`;|J|4iJFzdT_v zBLm(@UC|;mo(?!x+y!#-h&LkM$h5ak%*u>6yuQuSnLFArXT5pMb0zK+*n|bx{s9Aj Bm>2*6 diff --git a/software/LepAnchor/PlaceAndOrientContigs$LongComparator0.class b/software/LepAnchor/PlaceAndOrientContigs$LongComparator0.class index 3b69197586dc12c28fb9af9cb1a67f3049bca00d..13be9af082d95ca3a733b8128376d1eb6fc7ded9 100644 GIT binary patch delta 19 acmcc2cA0HMFB7Btbh delta 19 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 dkG3!3?D+sGAL6?N-;eSA6ja?&_?APjEIm zTbb#Lbo;ff9f|f$@#bkWQ7ecxkBz6+b5`W5Lph7%ZFs(51xQHQzf<895!j;(D8-ED5&`gl|8qNdi3`o6~3mNSe#J5)oU=`<$V)ocng8k^hM zF@dwGU$ntRNz90b+N-sRoDJyJLp)37L@)E$%-QNDy-bXdvoZY|;Od1mq@q+^yrCi4 zTo0%d%+_?Yv~iZ-Be!?C+SZ07XGis`Ctb+Dyb4k3G;(H+*SFefe07k5Y7J=ZY?I|YG^>aovFlJ!ZVQCvN$sMDrB|+cROac zHnphM(01>cW1ubZEpytN>Kict8)-#7DMgPo?h`s&G43!@C$4W##OrZ0q@}6VTjANw zpvo4|wljwa9@4_~%pb5Zk!YLW($=w6bpY~2OSyH)7OK0nv!jhLB?tFy?TJ(h(BjN! zYfo-L=Avb|HnlW$Yz4rr=49iE@pa7@tsk$luG73^CkiT5FB%z7r4sD~)zrME6g2== zV@IX79K2(uaN=paL8etOO4HlqQX&0@1TE3QwZn%Wcfv)v|_ zcYrTpK4@fBh;cU8W=FCz)CVLIOFCQDCEA@a4_K7Mf}b33Z=xYMnYpN6w-Ix8?gP`+ zpu$YDTALliYN*ZQY-X}RkSzw?rWvHeLBebitEE!QY!+f+N}XUcTc=L6S*}ily%IhDFFB|76{Dz-G_U>_S^LzP_fN6JDiOO9Q`Jw z3jQeTOq-3QtYzyKzxct|Kc=j0HXFr8Q0g3;9ida_*=#f`v)K7uSG&+=V_3Ol>^sY- z;Keqp(7j%2v#~mLxy>qd>Pnl9)2XX%R;5$d+HAZ|U2n6=YznnqnZUaTZ7@-UgxU&-bx9%0I3bWz)zX zu-OC}dAKpru{hqoG0_hAj4tabqq7n*;B{>ON;;x5Q(2h4NOuUzpE-??@DZEsW&1#% z@p@7){P5Dc$Fwo4!t4NhEXW?kGGwX|HhY{M1fRq_Tn0a|%o(;{h!71vX|r#$r_d1! zb9O|wj(lt7^i3mt#%AAP-vvi%g6+Jx0-ex~yKSpmDCU{Rf%7S$*BoO?>Wxjff9kit4Og0W>&tcuw zzTQ1P`1weRrD#aDZ;rRuL!pcLr{Fa*ZT0F}{10G&w9B&=@!@^CFfGSFZT2q$wvlQ& z1LBRd4{i1V`-ro{v$o@Pr8^6@xa7;10}5yw`o%H~IYp>5rwLppkZ7$}iQ(Dj@6jV? zW3zUdW$4s-kV`bKYlpeurp*I9h^(g8R8u|pA#tx-dJ?jEn4W|Q^=qA{rWzr}jNxJB zTADEM77X0w52#NX4<0SYBS90B%bTzSavA;dbwT!VkPm=jN z1B=Bn{Fnp*T;=kKfPsNuOwd@SF8*n&>SJwQPJ^{m$>vRo1sY{J8=Q4y)5ZXv*)hyF zXrgw`C)j)|`4le~YO2niYV%5dB=peiy#fH}cVyOwzJ93~^)Zb| z;cp?J&3OZqkI^NMqg7CSF1Gm+>NC)WSyQJun%Ep|I)j^eh%{{sVd{v_n;}Y}Tc&1t zqRmetnl;s^FvhlI9gXP}g5@|$t)kJ<3Z!T=;y^&9osk8blkxgFq&62jJ^Q5}VKZL~ zis5mzQQHcAeqOvIZu8T4U7BBI*2qRA;OdKb!sZPmudyF!?ZB=kbzEXA<*&D~3>#?l zv|BjRKMY5-=q6P+PEHp26XXySKKp~-&xLs_Zwqp4BO__zO-qoJ%}?j;AdOU#qF}68aR{*Q0DoRw-2u-6!HL(}MgAD3GdaADHHJ zHZ|8L+QWP!Kg;H4^RGg9DP3?Wc6pztU^pAqFyG3z(?(*q#m@yQhbUu*&Cll-0Q2H) zZHhfs`oZbc^aD?4Ipdi|O?-`-xQMfovW1^r@DiI}N;;I)k<_h(`Q`k|Aiu)hH)Mjs z=2x-Zn5WvmHn&>*8f@*AN?H>mg|s!1>`cXy?J?Q~H)H%T7Q~cFLfcBDVjb%fF&KVg z4Nc97FjnO{n_tiF#o{HlG<6UxZzNdm?4_VM)3*3coE;V;?1Q-~Mu<<1wI^FLS_X@# zTaV0SD%k>ABa3m`opCIwR(Cl{pFvz%TxS*9vrXck4Q}LEWrY_o? z+>FQTn;J1v+0q2wN9@gLK?P)B4p2Dt1T%G0)pOzk0Ck1jJ2M+`-V}XMlo)wWZS3G4B8qhxL zqH#8*ZOp6M~oy?w%YT5at-Y*Iu7@bpTC#XvC#*e?cS_E=Pu zpsXRb7%EDzoYX9-Mx2#pVUE7pIJP}(T>VFaOASWFW$f?_&gq*}MdOmQ@*gC?#v z>L(CipX~st*K{3skAI=FW0@sp17<7Y?X3W3O)RysN$Vo*$;~k+NCJC<^ToV0?)IgZ zYRT-WR*Z`Uwm6301qA~(@H!a8m(5;a2{8Bp%hUdm7;M)YQ#DYG#c{S+B(UiSY)ouj zP8N&84C4{9rsasG?3tjz9w1jOq@p7{2#OO>JT1t{q62qRSR5}-w8cqeHn6}qPA2;! zWv+ncBu=4CfGZ!=tek437Yb}8&KIjmFU%;?W0qLx(`@k-hleFoXnn0%7Ze~2ZA@0} z&fij(Xd@lK7IjoKUnzyl$zXnBJA8|l00rbJ<&YFVQuty@B5YZ%ohp{166F*T>)L^N zn!kx#(S?8B(FN-sBW*`2~G}3sJ0s*t1i4I$IicNsE5;z%l!bOhW=U5BL z<|ww^gJ&v-YP2Z}K(Kv6LlcT4qcKiWi!RiJ)?Sg}q>z_hO5DuNE_|{r zRaSV1P0?Kgw^PO{C$%#iKU=9sfNxk>YU@B`^yVW!eu^d;;)0 zt|&N86fBy7hN2lM*-QvW=1t^S2o!K;o(lyj-VWgxZ?9W_LJvQHhgj2;ZE)OYQD+7c z&(synIK`n5Az&v++@LwpfQyL3m|UI>kNkMo+W&G(H75(XF(L$TtH5?lN$%H>eG~n?Q?c}wpuh;0x)lv3&IJGdHY+&lJv1N zU!|&k#?D+92qD6zMWHH!ak5T)+Nz%9*y}#6(WIAp3>2@Evo@%)u~#EKXg=$@l0{+6 zJOc9@w)mNNiBi9@#Vg`fBKhCi;&<-07*hGs0ma9GJ%W4@ax?t~JCYso<`qc6(8y%HdQ#tk(t>_a2)|At z%8bK66(V(5LEaJ{V;S9TfhQ(nbre#fwg{gB3Dg}XESb&kX1Qc*^VV2J z#n@Q9wLVr+3F92adnuP*EZz(=3hZZ&T~0NlEpyV4fuJ-q?4>$r!fW>61NTXczgDK*pMwNFxem%-(-%=5%;NZEF z-vuj@Jj#|ev;)ZnFGIYPV`_zUL{7Km42E54Kx-=~%wQHdAZNitD6uOuAXDIqJ|O4X za-N(IY*)jlI0VehGfH95mOKWd9RYJ=Y(~tI*kn!DZHAPdxCZ8z5cEWKRnoP>huqp* zQt8h@pNQ4@4`OvBc4XQ_sy18XB>)nUL*{@y3M!jC9x9tW0i;E?pIXYnmg^dMf+eAa zOmRqSDUlXz-k?So06j^niQFwQ%pe84v0Ibrl0c)@q|(C4G!Ag786W}k>XtM_>Ar~~ z(mZ1}g4;t;vqq`uL^<$bT!wp`txt?2~o2 ztXIaq)Or{cV2j5T8*JGqp?(J1!7GxGpz!#EqaKhOY`IZw@yJ@fG|fN=B@j3CpoF>$ z)h-*a@5#&EeP<725g|^SEl($xu4e#Cg~5j4-8ow-u8~35P6Sy{Vlxw^0Em%FzSOqX zC$=D(2dsi(HNvt}ZXtW;U6eY*mS@VdK(2MLkaQ$eSyhYn%wtyT@n>imVfj_LJt()i zd@wy~ayrR#$Z77cm*MMDh0|l9jRW9yo-KFC^U+`v0_xx|-@s{lDu;fk9xJEQ2J0YWd z6g$I`w-CE6i={f-+Th&-YTril7(?*51CZ2M+2n>XJCQ2Rl$N%`LI$#HfCAcFtHDis zS?xSp(bTctQ|uzXObp1&2WM{fZUUgpIkdG4fFxBSOfE-dfB`8MI^kcEYaQlV=}%F13*8qrLMUX&O_jbM^A8Ob5`AJkJ(Ghbo7}7Eo+IzAyTI+v<@ap)w0y?DD^W}6(Eq4j&1M;~Qm~%0 z<@c2~MvPpKF+fQg9HJJq{Gg>&d+VO}Dl9|(gvRnLG%XKo>9M4@&wQhVD{3$MHQbc) z=eB%V{sNpm)wwRE;`Wu5(v=07U>yEg?W(Z#k)J_&6+V0z$hJGQ?SdKDKbm^Hzu zOTGgNRYF}cOTvwJNi6(^Qh&1LpJ{aI87xV*YVM_HfHHrz<@@pjl7X(LbGc@%eZ_xG z%g*I3`FDU}gkr6+6d80ORJi=7E&nC|jd3euuur!2;Uvl~SCqK>$d>;hL;GO#QNO5Z zW1@L0iISFe&0Ccu0k1M>8HoG2yxpV>Fe_6*2c(@O5#Y@*ZN^maZ~}!4L4iVmTM%x~ zbuf4L1&S=3N6A)c_KyK|v+ThNKSsGu0&jqPPN4M>=z3GT4iX=Q65o`8uInPMv~|qotH&MF#~U zvV8Vs8SBN&2&UK%7!bMzx`S9pG8t=*EA=k71uEw}9Fb75x1Mf4=yfz)f>CBfDVHRd1z1Pob&`x0=nzs`73c}IH61C-0E@Quh}SG$dh+~`WuT&)vtUu} zGUPxvF7%bDP}vyLIxNEpq}WK!hZ?RmH$>(>H)$9>6$XD>if*i?i|l)7sOl4uh?0hDAh^3|d&$ z-^J)d$n_#vqQIMox^5=#M9(E8PFc_r-(g!ZnT$Q)#Utnt zTvNQ+)%!5R`)%U^iVQWC-WNFf>pSUhmt}klIHpg|0HvTH?ZudNJ+i_Y`)y-`yeMcq z3Lcm?AJrvW>pI)v1MaAT6{n@Cu4=ZrIEWTD4zSvwaj;)?<|LB{!)gs1k5ip*W1o}t zAlS&cu0BjGENnb!JWW{k3}-i&^>K8}2k^9Sh(wV5MvZxJ>HWNadn&N9FsJk^OB08N;)K64TCi~6pnS;^U2+JITOBG2mwd@$y#@6KKR-c^%ov&V{q)B4aT*CLJCJ>v36%-X61q%|O zs8=FG1!hucP0GMEheG_9Z>AFzAbF=I?Jp^R$r31=9m&Q0Ie4Q?dOEGyY=P>;X+gi|pnwj!xsB>9k!lN?*i@S>TNfq4Dd=DvT%%W-lWlW~ zS&iA@;5=o|L$)-`BW?32^1|fQt3&c?@c=JqynJ|3NpTIJzaO4)}7YjoX3U1{T z*tS_~o72qcv_{EBvhn~>oCzz-dL=NQv3|P&y=}OyyP+)v^KxE}7)DpNy zJZlx7jW}E7>3y(JXxFJGyf#1$2=Rle51PS=wz<$ej#4L6Y6+~NCQf#pZLTD3FvFwe zMCt|2)qUzH8Qp}P>~@oBu#se2Uo?SO&#aO`Z<1+kvsd-Y z4nlDE#W494u$aNx0`T6dvE??kk|#3dT9FYJ`q^D)f1H-K>3*}$uCF1Aas8}{WU9LC{vTqZSon{A#U@{paL6F}Jp zhL(>#$>NL`>kOux?C;4b)sNDo;l}(pTLpSS2O4wV*prJ}c&ogZ1Ce|O)1}vP__%@h zn2JMh@ZLB?_u#|ct9=CdiNjDKoL3BCIr_lPEGz_ZdTA!%NP(;z0{rj(tj+epvwO8l z*6yraio)~yJBlyT8ICra58CEKbi6UAW$Uu^0ml%mVRMzT0;vYxp-yVKR#0`2And-c zX$?f+1-*<(J=L0Q^P0A5p)cF9yZ9mS#j=1Qy!}fJL`P0$8BDLOj}H}8Ja5K=_1|*T zd)j^U)YAr^1wZ|Q$w5y-r2dVp@Ak6x?AM8PCu(bIK5CnL%=@6kfJ=Zgcs2kRg>qbE zSxnDETJ^_94f<$Lh=RM+P3osbE=@tsVsMuDzXDpdQPZz01NQg#z#BN&84iQ+1M>gM zi1j)hvdz8bKJ0c)7_J^L?y}4u!r((}kwQaZ+HS|D_IYTxZnPVypH6{0ei2e}eYe;< z1nQGi8|o=m9`}XHZ_+?5Idy;(%@htNQgApp>Zfn^?&&b}>EZ}Z#rFv%UrmQm_;WBx zaI{x!^HuYg*zr-QrYCWrpt+8sj4D3pd!`H=_gZVdW}B~@4(gDPnQC zm0vixc_d2Tw9RMC?*QHam41~Xo$~`KDKmghlSx{qOq8mF+F~>#z7lY!Q9vXvhpi1; zzXRqww)sagQqWmtNKWP7p#1l2^H1iV0Zx*M+QC6VqjYw|{HtxguM8Vhr>S*5X%Gj@ zzuD&BsYU=bkY$;$F@;V<=r&v@jlc&)B#dw^^kgB3t%KH$P_xz;-ruLjqr#rirSnos z-~k0?xuYe5cO(d&bo3!2HLGxUx5U1(sxBilHMK9GDSl#`pOO{U#N_8TK}`xUJHP|j zXOr%TW`W%XA{iIbBf}1uzDM+ym_X1rPdD2s6|&7u@*2be26F6xO%WloF1g7HKuST&ojU)U$ zRV}J}G}WC&e)hi0%%L)vUj$`(w_*k66aLVW(@RK_F)MJ4=I?mzV=UR=N(kz$)($Lm z9URcR9UQd+i-2>Xv#h`p0C8M%oL-Nz9Y5kUD{ul_#R~mnO*;OFb~+TP9yk&F!f`&( zErjOCj7X`?2t5V3;o++502|VPPr=80AaIHuSV>e%2T~O8Lu}fCz-l{is!Pk@Z?#qc zMju{o1=eC%p`$}q0Fudo;{xWfJn+2Q^ngH0P=w*9K%4RO8%Oc(DR@KFHW&V7#8uU6 zlyFyksdBNmkx2;11I!C1Xg^wu4lE~yO!7E821-QPZqYBhgQE|XQgn!>u-D3}C#Z>o z5O}WXaXH?7mT`mU1`hcmk1n_q8<7?pk)pQ%85_|p-A|&Q$@od)3EnTZ*?}ZRtLKbt zrOEkrTvQs|Ly-GMB*5ps@T+M(&pspxzsa^SL<}mzQ@xRALZudlbxHUy;5LJBwgRv~R6Fg`2&uaXvDI|J z`1H`|Fdzyvn7%pooaCv&Db;;(=me!Pm;bX~!GuC%nVk}i$19OfLk)0yv7)zPKXil85EprS zjoFt$2jaXIfrtuuZ^hfooHEdV9P>->A>x@MLpt=!mt(6Zpl1Yy%yuW3eqr*A@gZNg z^O7p6gM$0%g~|%~JlN}I?&NHtQ$rulP%(nN7FWrKm=Y%Om@eptcp4zP)5y~MWzXqm z>2cx++m`f+KTS+H zF^MX%GRWE{pw7fG#7w|4tiT>9md?nb%4%z1dce+;7*-uV(asiV37!KorqA!kn$?RK zh!?f1P81CT!KaMJA7FDo-ofj%gWAmryg?&N2W}+N){HKQVPp5RRgsfI*#{9Gl^(C+ zB=lN{o-MlL^-d~kAwoqdutx!yAhLR*9^}LDq@uO=OQtd z>k+(~+CT5?Ez)!$S6$1qZd5U*7Hvq6tsXy)AUWZ*SecW8(l%k^y4ZyA<448dK>$8> zY^6N#`+ay@g43x_x;SRr^mLjUKpyE%!4rWYoAE+Cm?YBS`;d1!Jd75F)M_k*A_WYI z_~QYbi*`E0zO+{-&LbZ#qExI+zl&Z^%bnB&XG(Z022zz1tiWSw=yhhib$Z5lw_Uo{ zYwm<1v2{t(QpvxMxz#IFbVi-%*qlgU@2rR^72CWXH_&&Qn(5W)e%=7D#xkdMFA6}@ z8QA#*Lc)pkGsz(`uS~2SuVETXk0#83dJvWNcMQ|(Whq47n}Z1se3)WK7nt#uj&fqV9s*%=mpXYodFb!%QKvPCEd#+P{9AJ14q$Qq@=n4&3qTJJWe0(krnSy z#*78Ei{|Txip9B6eI&H$K%yq(gtTXYPv{ zdv(#X+3%;>hHIJe4bzzk>75WF5SrQ_=uDi50V)g*y%OhikkbmlJb02fs#ww*o&&3&6}xW16v1^Ge7;R{6KN z1md1^*2>F^X_WKEmL=hD3|sLskZPXn+I2h+2r@wYo5Ef(U8D-)qE5Uu{l|$JTx<_6cK2Q(l4_EATRBtuC=5yYv;!-dKsSJctWm^-pxp*bb z|COMoBdSfUiU8amX-G;nrnS{?8z5!d$B)6Nar{(~h<>#L(Jwa5EnwsnWxm?yu5f*U zbl@uRnjN@+)dT~-#-m0ah>qv&ML8bdVh!9E@TVF#agB%IuT|YL8bcJCM$@kiE04;7~L_ftoL+=7(kX zP1N?T={N!Wt_CX@19j7(WQ80lQVPBp)6v_No^%LfArBsIo8MsfTEUTks-u`u_7S%E zb+gL~mI3QXxum>u+r-;;t>9Q72~;P_8fTmLQnB%{Aye%MlsVBh@1e}e$W-bxWmenf z-Rxch$dPvNC^`;HacwaAVMn*LLBTy`1>p)8m8ucEef%{36)Si&4)Cr_cIwwDfN=ne zHbJth*BO(7MEeVH#THWRh`@=LU{9&$HrqDe#!H@quvJdU)`m|>RMHD9n%YqBQik0; zp&FTQv)d39A3U1?5M z@OaE=;uI^m9AJZ{L#0l##Q;%c1y=w`6dYIDtcyUq8f7P|A_y{5>jF0+pG0-yHrqo* z>d*{S8Olo7=5D|gsYctpOQ)J_cAp+DzA|%Y`CzvPvw+-nD~58gnjZ`g8W@K!6h2ksOKqv|O|RIFMM8+RG1 z2wA}g0so3hEBG*FREB`&8=U2)fM(EH7#DN~{iYeRQJHSN+gx=!(Yu#T6H^^{h z^aV0++*1n%Gkkjvxm&O%N3j|_nTEURxYA$Y0KSh+-^GGP7c=Cy*qn!uDcL;yQX>pq z&d0C7zXkYbGGWhxM8?M>oR;{06h}cGD&NnJEyv3@7M4HG7OmXRmh5Fq)5+u0$>r(f zN$KQ@baG`nxjLOZEuCDGPR7&8x^yyuWFyAVw3lrt5A0>l=ZKqP_mJismmt9ak;A?x?MdgLTpX_Cqlv{h*W#t9h~II@49qO(Tr)l`w;3OmI~U)7y9}R{yAexw3l{AzEZMzSvisOf_6R!~A0V5>o?&zFaj$vo z$5@w_*s<)Ftd{)-t-Z|_vp=z=>~Cxt`w%UEVmENhSw&kf6x+#msWCzULJpzBOpKV; zI8Z)xFS{i@gRsh7g>_%8@PNpOFhjRMDMCU*2^a`@1w1|0aqFqRKQ+sVUFkaY^xq^CHiQ~9T!y8Y;9fEv#s@LtErE+zK*q^ zaY4Dw)>doJwwlpaOCN20(`gHOakjRad$!evwod7A}!o!4T`~a8`-S2Y9bFrM+#RiOZ?)I^7t=J)XY560p?T+q0;j*-1AKSl=J@FK4 zTvB<6)mQFgPuG}T?D(kpD1+Yg6gyC3M2&syIr@14rJuvU7Y?u&cd|-Z9q3}i#zq4B z*ehrtYD5Bf_^W;FjeYE`6*~ev63{)pL9M}bu&eu7UE%+wV;z!cJ(5dj!9!v4!XMhBGo z1SzRfp8`aNN_8V;DsU(^hEo3_v7-j|kvlLLUI@*)Q8FL3Ziob`tuA&+MZ_W;;G!lZ ztHWa>*2J0|{2FT~*4UN$nk%dGx>!XtCz3Y~KjCq^Sum1U9gfGk+z<_+M@Hv%tq0y}nrpI^`FK%tG03me$2 zz{cCy`AA=iHm+xPDiGfZU}NeYh;Q&fe1iw#8$1x-z+4dD08Akeqb|=uidOapZX<;c zbhE2@E>adoc?Qqp`H*K3wwV_oFN_hN&I|bfmV*5|D^F2W3dw|SW;3HhkSKu>z z7>e>5L+Oit-e86)41F1G=b_g2h*=%%VjUHcpo5{7tPYKh1Si%2PlJS^IkMXBV#A~1 zh&`@4Hb>-`Wx6 zkzgcnNB1RZR9uLqWe`h2_D!Vj$MQaag?$i9`ViLRVW=hF0^558qu&d3e26V!``PjM zFz1QvQ3%e*SS!+I|UOZd9-p$=Fe{oKEdKx`7r#uAo{2tKY`;f*z zfG+(*$jRrRHU9`&@(YlMKVb{mi$KYrLC5?NWaH1lC0~aA^b4TnE9`4XUjdQ0lfCBS zl)F3>-Q}U^E@vkEtANU56sN3a;~oCEAq%I(=saZgIOT+Xamt*};*@wvn~Sjgvp8i%Pjr0) z9PG{iWlo9DiF$+4obu-mr`)DFW$LpyC2_&eW0u_!`{wSqz9^%-G>wUxQT`5?_*6ZsiC4jGkeiKH!*OSUxZA1)XZD~opvG!SB zENYy=jAHhM9Vhy57UE>lI!R z%&%8?B>|Ny`O)B22JreEC8$m8dfJt~NW1-dv`ZQx>=%3%QWjee4evdbYDOMui6Ng7 zguXn?IgFXzFljg%o6_mDdWWru_?Qjm!yrFr_ZKbZ^gs(f_zR=O>|SUw4k$=yu_P;6 z5FQW~%){1Ft!<4)h9Oa`JFRjk5{d)}3qsWa7Yp8w1k%enj+O%R41g#j5Xr;%2zD6A zybNMy9E8MFKAO$sWo!=akK+|;Mh$vI-tsN+mT!r-d`rCLJJwsiqnzbCj8&=y3c$Q_ z1YZDA6NGvmQ=-O#PM5<;L-H>gdhiylgvoBKaK@sMB8~z@FdA`uCDqimR1K#Kc9@dv8s3Whm05XkBp~c-H*b$*MN+tgN$c_jE@Ex&jK0G z1{og>GM)!AJ_clbEXa5v$oLqL@uL4387ncO$QW4lF-V-qm=u!#V`LoSOZhTysg76N z{S@32r^$Pq@s<1e35zPq_w(gsZ}FK5`0>o*C-@K}Pa!)aO9MX{J7DhPEqDejB&8c4 zv+mK`rQl$+i6|i4{50Fay-1^iT`Bs5aV-UD&@w}2?G2#J$3A7h7cLOhESU@`pI@`{oJ zd<_}QA7Km0uw`_yB~imQY%L-C7Ytte_wo8&>~QzawUzP470BA43czmWKGw!EKh|bZa8uA6+;7@Sc)pPjA0e*vOeHU8S?W8rclOeMhU&BItE!bNe40Row&Fk1= zo`8>R$HUCx_8R2$g!d?VY#o7oGzg>~Z_pBB#f&}Y4i@R8$E-Uw}}1MF>+ z!h{omfmkya6OhstX5{gk6(+>kEPBl`u;3haGrtw~X9=s!75p|3oC*1QA-|p9f%G2O z>ZqLMVX`iTNF`;_yPJi(F%gsYmKX?#R2lK6dO6yDM~1xtUY;%$A=8QC)ToHcsEU28 z;WjZM%QLsJ@bqqst?#;_9$kUx@KF|z=yod-e*pJHbmdBOn*kZBkh;jjIkBX&WFNoV zTiidU7q6IV@-sk!XR;yuEH)h1G5l-={Bj~s0MvCmlzU`2lEe0fUWYJKDZJ7HDwR)5 zu?Q30(4-Vl0e=mVd`tpgF4A-flSUB#F*d7+e`D-EzGo*JY#z*6DS^-rgyYLrf?*)F z?7<5BPBqn|VpHWM)Z=24N+y=|HyX+h0E0A^!09#|!^hWuQCt~i`Qu8ly%)3nk-B(c(*TGD9J#?QNAPa9~3BHpx^IfbH#I+UWw}YTamDRe?4i9uY_*0lK zfo=zX8o(jY?cmQq(=mt;`FDUFCUmZ?{JTg6-~(RApG7JN*W3zzh(7@`EW>u_d#D?N znsXfgK2kY8EJ6?b2k3zel#9sNp2uOKtYTqp`!UEw6yygDVCY(-8=dxkAPj@&(<5zb zZ=^bCP0+`&d#v6coz**-v3mDpUp>QHJzAJKJ(lovy@Z3jCCsJurq%1UYzt_u(o3l@ zU?Sb$#8mYJEpm_Q8d-X-`pS$|Uk4vM1DB9|H&*=~fUXOxelJ%2>sa+~VAa3LCh2GkZWpVL=4v4h--lfuHKH&GzZfz1^Pf-6E6yWUP(=yhL(vd)f|8xgqQo!ZE>qn^!u4yhy&)SigfcvJU-9p7l%WpLl>86` z@%Pw3z$waqh&6|y4ilcrUtmY`pRkkoPoagr$Tsqyv9oZ00slF)T^Q*2FW9yGRdy%p z-_Nz7?s8a~02FV1AL6e=fD#Qo#DA^$KRqSa88OU<)asMOcu1|eiQN178;bq|aFvqV zEeL1)<@`;g5X;N7Z90U#&_@0n6w)=3%26C+9SeK{Fo0tm&GHn-IDpMN`R5ehz{T^^P3#7DvimFpai8lLoPAAz zi-3>d1DYNpy-kQ@{Q+DL(IWB?ya{l5$?BXgc~3gKP&IN_k4Dhvt*Q}BFjC-* z1Z+<=Qsic;kreQVK|?8w6ksUsBN_@M9a?z`L+P1I^Xbt_H-V*;TDglAXOveHdddaQ zK+Jm-Jc^Mnkla9;c?82#BZ=@xB$B_o`#so)`Y6#&CGPBgFFF8(=r$Q~6UA;~kee7B z9Y7plNEfS#4)HiZv>bMDX7Nm z21cU<^C2!Mhw#XdNTCDD>?OLX1VHJ6YDjblR>S3_(IM0xf(cZRKzVcs@ztR)>kO@j zbtg=ha1`CW9~}}MsKz=ZI>@<3>FQJ|L6wrVaMJZCJ2*?(QnXvTwmQ5chevW0OZ|9- z+N#34x6+7&my$}U^eAXSjgY9n0e1y=;=jda^>>h{zXx}Hn~mjv0Ec}Cdek4;QT$yt zoxcY=5{%INuh45?gyt|p^S`l`{3B>V{{gT27*h5VXi1-f>vqG%`!RbEPN8oJ$qosF zy(CQb3*7%xSnNX);yEJBqaufw2#Zg{yX|I*e15zr;A=!7Ux)j1#6W(rh~PaH#r!%k zh~I_leYid-qI|y?!k@+U1yRah62tlHBF5hqhw;CP!}*6|l*kiDh*Eq4exxW96T}#C z6!PYZvEn#UDVB?I;uKLOP7~wBIx$IX5tGG*Vv4w2REwL$RB;#b?i17SJ1$^u-6h(&UQSSrVhWpXCI9J)X( zmy5-T@GIUftb)*&Y{)s$DE2K!5#V!ab(qIEB^)KkCL6!Qq z(pIJXj*?L!7LZTz50$hE$p@5F%VC4?9lVc_XS2iQ#Y(>AvMPBtR+}8~Q{-m;U!?Nc zeA$TA9tLIOIDEJ96Ql~+I=K*-5Wuc-qnr(V2(Tio=iwM`08;#FY(L208{v<5sn@Zd zFij)xZGH%tLS~V_@+W{l(6sr7{1ISLfDIOTtU}4qs3>J6!h{SRDkd;X1Q1A2Dz5&D5U`4cvfJj*!V3v}22hT%Hw6IMhzK>=LLFwoI8&z>Q6V-;}_U+2tlCTz@X@tsSZU0UiB|i)9rZlm+V*rrw0(O^6NyIKv2pL zeZ1FIenh_70$&WJ4kY76nJs~~Fh?09%q}+84ISeLYmCYO?eL?9QKk0x5*KXg&8lTe z0-BBl{7DyEMq6p+CaR}>?(~WX%w&itI#?5M+6Wj`(STW{Z2{4MRCn=kf?7nrYH^3z z9Wih2ezwwM=vZSL(?D>p*vf2i20Kig$!6kutT>AuC(dTai?6a(VjD|{?d(dVZxrXL zEswHC!5-?GM0P=4)*Innig~yZV-#0*7>KL@%)>ws#4(}}1Yxihu#6EoQ`BRHdYXebg;2s6jSi*BkZ~g{4CukqSb*&U>CDgP;J`btc*kALY6PShPhnC4i^`* z(c%&X32jX&^B_^?TIN_8*o|VhNDV`ZHV0*5IQ*XmSVE47DJBbYCCGgYJs+dR=_Nf{ zIRm+5r@fL4m0|=8mAuBRC=#QTZ7BM<7`^gwF=pk%qGG?O+Ak(jT;f2TJ7p#Erm8$w z%Ya?ANE~@U)YJr{!S6AcWG;&$U#=j!~M?gJyAqA{$nS?g920rmRxn=8{IlEW93By)YZ3EbakIy9u4dl^HB*3 zjF;P}bJaYL#XQkR-9EAKQLzN??t`y$ZPap9$c;5YL`;vN0@UOaPl@CAiRBk=th=0C zUnRR)prksapGHCl#L4?$rFJG44f+kkk(!(J3!PAUIc#Cp*L8eG83#0KD70FeS$h>f@gc^+uL8J52w9}TA|umrPCK|r>MR@5Lb zH+jHSO_B6!mVm+tqqniVkJ;Fu7+vr&?HU^qw}ju3|7CN-|6v0lL5J(jBn-t8rSJ?D zAve0=EOf2F&I4^ohG-){pbB~FU8zvhKRtliTC7DBhiDV0dyKiA7=XM73?&AVdZ1Es z&yC8ITB!nbxw;90`zpq&?m|(+_bjK_EQZ;!I0uRraO{%vv3D^ZsstW_5pRUm7KB-y zi$Wn-tAYj{XsFJsEO4GvSNW9%IuKN~UKlm}4`HSDKaEt#;{0&DxNx7?Jay1mqqqn+ z5xR*M7Z&Fyu&0R>7bT{a77r~Rl%Rc7ytuSDs!}TurI)vp1trE{oV$}9LynP(ed0__ z0tHB(H5RwPX_W>c)C|Xz;?d!TLp8WdM2A;8)J$|#Vfe=*{o`>*u+kgBQW`;)o-2Jl zQ@US0r;YTRz&FW*l`9UxW&eF}(;u*E@jP27egtXt0$VM9!ZwH(*%t9Lc7gagyH32! zc8gcpL*ke0N%0zc7E0Xn;tlquc$2*^-r@trZ~5WkcYLz=E1xai=S#%jKofuG8^k}c zjrbS8S^S&dFNLD4my~zs0gtjCfK*gQApQVk{<4%xCE)8?0NQEaVb)zd?1S{ ziXP4{cT*l6>#@Vx?u@+zV<$RJv0yijbtsY~yO9&xZQzfB5upb|Q$LrqM@Zhx=bTe1 z`a=LoH`@A~LV{y+aLj)Z<}So#|6iir3|jf$q}_f~P$unW@RBb_yTn1h4DCudhhz}j zCAd8#@L$?&q0Gf5FrTfK1#E*Hz_!RDc7cqr>tr$8Eu*kS4`ENr685YtWzWmu>`fVC z@5{saKskaRE=TgovWm}^aY!IVBcdyUFFr(&md)?m_Zap&CCFU zgHFLEm5^!(Wq4}^o@2WNCsvn$5!DgtH31dFDRSV>3f|75ka0^%e|-XKE6N*pK`rL* zkq<*j^crid`?MxBmLdeGU`;q0B7>vd#rH*Rt(PJQye@uyG|$V;jpq2&iTr4e zPn{@?=K7u$MRR@6BE|U-@0+luOPtXks2QrBJM*U5 z4eMg#qQkuBrO~0$(!22u4Z22iqPhFU4dqA;jbh$NQ&FW^|Ckz8sojX{_}}k!r?1zY z*?OfVq5eN@pe0kO1^N(zamOeB*kN*D1X8bHXmM}nfj!b`(SV*U-&kYfVI4)M(5^%8 zqDpiY)p8#YdUml<`T=dFmS;Q_%hx&z7PYKEavK%cCvHk>Ie$^g>H=oTV_-x&mX*s| z7||B8Bjs`I7`d1&LH;RnDO)R#XH9ZBOUjd2r#zW$m#44`V1@|AyGlh^lgb6d`D0zXHFE4}%^J_3yUL@*q--`S#G9k{D_2N=_ zinv~$ByN{iiznnY;yHP(_zBFGf0Q@K9C@{j$epr8-bNf6QJq7HLActLELHmvSO8>1 z+zj?1SP7e`b|w;gl99^VV6wH?_0TbC$&%uJ?5@bN&@RTo;7OK+?V=dFt#UlyAqK$U zNp80XVEVlcc@}$&zbJ0Uvk(sQeNRwiOql(OcL~zqa@YsZ(MW^KMP$xarNQO%T)t6h zaD{v*KTTa$teIq6-Vs))Xx8)P)7*shJ_qnHf^<%u2kO zm0)Jj@8prJq#J6b6POaD5Cgppo8sD8kcZtFa%qo-Js3>H+SKAJd_-np3NQEs8$N8u z(0aKSr>$C|a0MqihzyZ#e-ZB_LAFwQ1a8?5vSMJqAsg{wfe~r-CKg-h%z?l zi`4T1mxenAMU1yU{lOFkO_FD>vEgEMioRrD|1V83^aWNu)Gs_r zpNjIW6*i3@u@E`9gBcKY5o)UA*>)G$iCq)N6T%;swGuWdU9i_U!B67r#Iq4X5+r*n zyJ@gl88u*og=Kf2_{POx@n7B82+6GtRk*$C4z?u{LI`Fh#cuBx_rX^e3Ej%-GPmk* z+FAt_UCb;XT_68w&rXVW7;T(JI){=*2)HqUGib2MC&mAn_L#nZsKkf#7*J> zFjs7V*-hd>Cko;w7_G?UOL-5&+yuW8YLJJCOt3d8Be4ahzD;}!bwg|&J5sU0Fpdk9 zi$}l${ZUY;2{W)ZE!8rR^6~DL6l~xCd=gUe0SZ~%+6`9e=)9Q^;oI2?4@d>At~mZ9 zxJm?^cKf_F+)rz0T!z6Qj#HdeQ4%$=geEQF=BYWAIHXap4eL55cX05i{E~u+P|dRQ}-n;+*2#xU=+hj|bOWc*9NnQ(Ejhs zas+XbYZ4PDxt?a^k#P)WCFD7tX5>8q-T;n()RRaBREoF+cmv82mw@7f6#1dUaJwuJ z--be!1NYQq@f1?IY?K(!-$0Fg#A&`Go<`t%AxIh4t-M(;3gqu;JTF+eafl~L(by4pRk#9vSfq9MD!tW7477BJ{6ZiS#|zIv6JOr zpzJ4XykfNd%J@Y-qv$I*?ocXfZS0+Bbb zXrD^SUCb=qQ$dj^>6}2s*n^_=>GMP?uGWCN(+IH~Bg}G*99CfDvLYjo4K?!F7^48d zEo4)TB79grmn}6S>?=kwLRAN`He)bL8Bv8vI8aLSaWM%7T==NK$0?6o4>bSNVelm< zS^--RvoERoLlJXA(X=MQ&WDK~BBh;(`e;AefI6)sXQA+a3|s8S2-E(Ajo|z^Wsr1~ z0p-g^7#qg{<-r30@#lL%e6<7d%|3|B&x81#2uwB&z!&cr$pAT4%!Rlgz}SNT!2U&m z-MDm5sj;>vz_F7th6CU+0Q@ijd;|bK5&$0sfFA*X!((HN0l-H9;1z6*F_x_}D%t7A zH~_rL2k^}vz&CpU-|PXr%>#I2zW|OlvIBf%-vH-B@Tx8Gqx8Wlp(0UnZrV`F1I}WW z?iVjWWbPL~t=Ew7oy9b&)ogTtzV9riH=6>)v1`?&fKw=jKS0008LtpOLm-7qVm~Jm zV^;v-NO`HO&@PaJi!1cfE8Eb)B`W@YEII6rN?BEDR*|cn-0FI8dAKrT8VecI*&t(v zZ;+K9$SW0}gRa6xikHC!=?!7U;ulEy2Z>VPRd~;cctyPG4f2<2kUzyBNv~Su4suDx zAZrW+9t_oo2%HE51PLI#K#f)9lY&Y)I&7d4rP84R%IR&)!5rqY!Nxr7pysp5#xZIx z`e4!`Z!U|RxnL}cXVcr)=Bimq#9&WWv+^HILU}bSY(c@*L6ehpXbGnIEBI;LwRoMT zY5fknbqZ;^idJA3GxHw6`3~{x8k~y-R=f#Yj0$%!s-Pdgg@3Oi zy}?~PVEyr70C3j>(0K4_DKidPMO^)rJberKugEZ?Py zG&K@a!{rDywZX+hiU-H1B1SZV)G(xW4B|UVx+5hwchARUhs28uP#?%lHtqLmYSkr? z0oA2l?COe$62yq^85Svx47i=mEG{AdOpTg4U}zZTTBObd4Ku1R6{{HV4HOr3BF~Ht zGx0Q`B7VBq9JEjp8B}l7-_FJr52j{D`V&e8Mlaa#%*G|al5$IxI^4v+$DAyyTyG*mw3eZhS+cH z5lrxuFUEdbUZpf{$Qq*6m++3aI+ zjpE~Zh`-&T_;@~oQqF1gk8ALme(^ z99F$Vl^cX(k>|s~PR7w@1j76SsiFKrwgwJ&a#8;sF86<-l|lSpd?%dmci_y!NBny6 zAyPvShxs(@IEbti#q67^y;4!~|IqdwfKgT1-?zR#NhZmg%%sqZgeql_2!e=WqbMpM zs8LX?YcGgh(6wN%s5H^oS6u~mtyoaeU0r+cy%%(qmhX4&dv7v{pziMfbJ@H*_uX=C zdH0@v*bSz=cA`6*XS6pvh>_qbkd|jBu@gJa^wCA^7x&RkyvOb^N@%s>BN$4O5?Y=3 z0b0m3j&_DhFs>bFzRewFW)vx7_$DT@t}wS58f!wXV|7Sn*Rl3^R97`a9|(FbU^|c^ zS|ScNN|R9qrQ#>nVkHzkn_;;T^D=*@0)**Noo3dHW&Thfs6xH5l_aa*Uaez8R5_Sn z?tk8+%gX;~kHj&n{^I}wMqv3Hh$#abw(Zq=92;u80<6J*+R_@P0LbF3K$E{*CP&EBwK5lobBti7F4}Qrt6T!6Laug;);` zCGJoX19PYDy+F;iDgbDvPBiEH=&96-7?YK z;Gk%t+JqFur=&226xf$>%8yc1+J%Z^)S?kmmeK@BjtOXL?gtQ#1Iii|UM}S!L%`K6 zbZ%NkrF@VGj#In=CgMQ5U`qtkQ9?(Wm`%6zV6faOk{K$=mBflp%cYwqQ7ozDW=e56 zX41GC!-_fPHqDeGweMiaW~n(Rq)RufI0jkiTnQ%DTEvECN^>Oz0Ns|-<++kX+$-HC zV3tKc;}%u|RWZ#hZa%Oc?*4*hGSyHSF9FzhB&A-_kZHo)WeP&rFkY6z`)m{6I>GGV zp0Z$)1LGSsnG0b?lUpWBhg4^)ORk(p!r_7mRc*F9>m4$reYP6>Kz4k6t`ZfhP^FeC zbi~_^S6}mfp>{jeu7zr9h3rsJI5i(Yr${e$LP0tWrowSK1A`jlOzK4aIY&vDUw0e;k%T&k~lpuXm%>RaAP{hJR~YxoGY zmT#lh@g3B9U}zio-Wpa4TJU4FL|Ajhuh5#`tSx@Gw)ta-nev79fQ>ctUvwa(t`nVg zJF$)KAa>TB#8ll`Ow(P&0lJ$wT=x*i>7L?b-Al~Xy~X9ak65nziu-kc@t7VU{;3Cw z*YzOruFi{3^6YKR>lIs!D*4xOO-cI(`+sgrZN4bOESx!Ux zP`o=Y=Brv3O3>#=S4!12;Yn_5`a#DpJrDYNG4{{6j!N>OP~ED`6#bk`Z|;HeMbn zy8`Fa>=>C9kx=_I85mJEE_kt0b~9MrYH^k9PFNl9Bo3CFfw}1M!7(2-;3FWa?*ZH} z#kUbV81Z%q-$866>wznk@+o3-*%PTW-&^#Ny#Pyvko+9k8#O9LXFk>7nxWW+_cyp^ zmDm~LZ{pSF#8jS>eb8IAIGlriL|(0UjP(XS_5iS}_2B14seiK0z|4SWNkkNnQsOw) zY$QbO#ay;8us?Vt67R-1X=kb6+>@-9I?8^qC?gJXCvd)Nj2f~8ldG%Y^F|yKQpJzv z4C;%~Xo0sSqpd}oW<1ENj9^D97eS0vtr3WiW?j~Ut!RtxrR!K3F7sx#A~LHcskPBm zCIPnjM|c9^JKIK5%#9b2K?MCp%LjH;t+kS@)zajqjg>%XZ^&@*XT*__q> z53NL4pJ@pC75_s+0Efm@`ad;NfQC5M>tLYPe+_fUt+2uT|M)~DD;wkdf23kkC5Uu7 zTB<-6kpcNt1$-77H}MQ%*+gRvA=^Nm%@RiS`!Y_^dLn!lo{kjB#YJ6DFurjuEnF`9 zH&|d?xMcbv^Q33Df=oST6-+%ugB|Mm#H3j&w+Qt@Xge9x4p^hyBex_hlodhfJZ3dLTd)E-y>J(A*UId} z+`_SS3RDaYw8tdS0?&e3%{>w?R#WRsARVQjrYGRIa%UJ4*SE@IGvIxy!Hs)cCdZPp z*vQyXkt_QU5)kA?4F=`F|tij z4Pr3RXwP3YN;Q4c=scD}$)iY*5?-Q`#{bnK8@&%&-;K7#euN3x;% zC^lRl&Bp3u*pd2Jc8WfZovx2(=j#*Lg~+=a<*wJKv%l*z*rWPP_J*F#KG$>DI(;@z z>vQ=CeJ0;g&*i)63;9HS5kFjC%xB>_M_`f}hPSMYn0en?-%SL&V*#m2NZ~5L zTXNf^VagCWj1t)|dhTnl`Z9P?YX3Y9X` zUwgXPjCMy$C5)q)Wj0ZP%C~JQzPwNiQp=5YgVPH^xuVl6bs3fk8X@}RgsfCaf^AQu2*!f85XE@YY6X^t5P(`O$-B)Cll3X+_g!_xT>HMn8f*(ft@xx@;9RvkwWKvy*qp{kCDB`{c2c;XOwl5B+t_>rC>|lk=9#!k!Lt{^W+T2WdB5#%k5T-A(nZa0VJWdeYpxbkQRWEn!FPihT!U3U+i;F1 zfb&y`#Y1|~N8kp>jy@vU3SP2{f+jYwpiIA+e(f!_FClf=uV&?NolMZ^iaITc)%A$!0{UvOa`2j~dv@l=|VQw}z-$8BkqP*w@ zKqAo^_OQRG`GZ_(uFT9JS~h!m0p_%HxxA<$@&OXMi#FVofPl5>w6V3Bw~>{2{cbjL z9;=S3t|s-w)wuNUlb20iCa-u16H3Qf4iStd+%2!9xu}ryq9c%>Ss|~*bN~yYy>Pceu4|Viu?K*-dX>H zZ>Rss$LeSKpY(J5X#G4tNx#T1(J%3N`enXQzrt_Vuk!o#YkURbHol1Nr>60*M~V+gP^T-g4zGmM;&o0(q8|k{2L-l~pe1TNQGNRVi<=Lb=SUlJ{F# z`IwcHtE?LNyj3UPvD(Y8t&Vc7)maIvi%MHv)n-;V)zj*(Mp`}8zE&S~u+>+cVf9ms zt`@@#54f@Hgh0yzgPQ0Jbguw+7hQO$K`sB81K>;Y_{=p!o{zGv1kB zVCs7CSbPXHVJ%=-WBD}R0hm{cpTq}2GuDGgxl3?jP(YGWcndsO-i}t%s5gU?T&WyB z=EloAkg5QqpqKm$>7haPv{2rOyi!pK6L4}7P$sINWqTJ=X)%axZEBQ@(ZKWWMqVY1 zU3ZaxmG>|YX0AKRdvT}w;w|XvE+eSKkLZVbOUcc^)gT9smh2_Amy?mQWq&ae8ipPW zDYgNB=Wi&79U(fKx}H1;P9bToe0dGr-jZiBUoPRpp^pe3F3=!UocxicR>h#nu-=HRcElVrf!+K0V-7`cw4SDMrztKm?miqM=RtMR*2k;nl&KoGoJ6km~S)7 z$P1#mg(hT4JnjcD?r5|QV2Z|5g7HM7aF`dvn5HSADlB9U<~$v9S(wzm9cG&6}B~co-L3>JVfYAQBAWwv29D+Ku}!BEK!z&hxjMXGtCI}KtkD#HyCFp zkZ;~*E3|4hX#wJDn&c4tosvJ^bokp4)21c;)}#~cWWzfx9yjKzo1w!tQ3xwDuRU*X$Jkudc=6nYsm#)sYPwk0B=n-gNs!B@`b_W`9chd z2A79Qy19nozkm*VY4RRJUIccYsfn>#v|$PwxR-;GjZB^>O#k|g8PVhMh@QZBsNMGU zr1uxGDj^4YQVz2m_~u}jI~ zlUWBb+d9|`Hq!7-6$YCs3^o-nsm-id6hz&S3OG&ql7Sqr?TGGX+`tg zw*Xq-f{|BWW7j(?^6uYaQ#2LmeQ#Dqo9d{qPt>e7$CI+km)_fs5ZyUvISB zMqk>R4Upylq_c}aXROcau{@H=pN8btV7~{ zjUsmjIXuE@XAqMXbAZF{{V# zf!3u4-d`P<@oga3Ku+F&p;U-?8+3?Lum++MNMDZ}DMj`Q`S=c?Y2AjRo}?11S~_Er zt2|eniToI`h4Vx6*%}R_s4Q0GtzS*bK>&lUCE6aBL*axTJ(tS2qiq~rd(mQCg~hm< zmBAa@wkV&8Y84ivJRV#N_>@Ce7=aHbOi=5W@5=X3ExMrJr_WK$!H8km!$o{S7uMy3 z;&p-dX~1O|Adm_5Yb|Aga)tchYE})yNyEc{%O)J2OXY`(9L>W^c)WzqkR!K_ty!(4 zmr)@pdG+Wleo=@PR&~gXjwwZPfQx#4m!gJ<9pI0U1TFb%BBLJs249Fp)@T zqV2Sr>ild5!M~*kc07Eu7PZi_zYf4|VAcZWSPQYQ7O@W2Vzz~KJsW8)VLMniunE?U zY!3`=GV-Tcx3B}PTiIDCdy(}QcA0f2TWsCUZnpl)ZnN%Tf3@yqE5HtZ&05CZwU)Dw ztozuP*8S{9D~gI3xd#);GI9aR&RqIPtuuFq!`8_4$dllkA0!Sld$a>PKyJWW3y#1# zntf}-NW?a~S|KiDe{5F5Y*xT9pLTkL*_cz#^g!DpYL~*sM&$>iJZ%-@U?^R*dkeH$ zEOG$J6d^wbJvG}MR#R?*6&1e(qS$F;Ep=_}9S}i$;F75Aad8{KCLF4p97a)rvj`%= z>xdJmpk)j*&&FXbv4KA>Ivye<@o5|n&)|#s2dlLHi6i0}AdAlx9Shx(DF><>A&DZM zEIJlC#7JT#q`e(5fyKuHl~2Aahl%x=cX)CV^3QEQE;a@A=&th-zYI)GC~ck9ayLb& z-OC1~=>G8wAo(#!sH&vd1udU7y`%1_!*i0a849Izx&DF)qpy1xn4XcZ6*iJ44es4B zYj9g^fp^y}`2dwpzLN@Q?1AqIu}3V_gie-)56-yZs9n_jAdH~UXml1uhwmFqT5kdk ze~VRFZ?hWf9k!JPl_cbiwca;FiVVnhDGX_s!jN{s>_!$qyO_ZdJ}@@kx)$5s%)Ex_ zO&?C>iVeu9jb!F=CK^ZT>u{LENh++x2DR)U9tEEI7pBKx;T6g)1Rtp?C~1kYwY`@r z@Z87as)-M&k7`kN0n@d`=P&uGVAjXDY(8OG>r>Vd&z{z2tiSa+E}JjScq4qM2s@8M zlHiIlOf9Icf~i7`sbXWId{Ib}&e0VUA!svVn!JMOjR?_077lFQ|F?!2Y;u?>aNk=G zGuZerzr`@W!!WYT(3Vk*3?q{pR4iOWFfNVFyi}F-f>UNlp#D0scanh4ZJbq*xjljFu1QtUmjhjh zLm@iJ*(7v>+AN;^ptFFBZgt)ZJ=F*SybB?Zi4uq?(XBIrwi&6m83-WVx=Jaz#e5wd zK{wQe9|BcT$nX(TnzDF(K-iZw||^Ykn)4*0e#@AV{lC1ZhCkU>zU7 zH+Ozw+w`uHcfdv|G{O!@1S@hC_o#5Gs)DX_I&%EPX2b$GNUn?s#9P$-T-=GcuvI7J zreXp~WlIZ$J!VJ{5-Z+Dvb``@iLT)?oi}gjDU&m*i4=YrV&ybt;)%mKhYQAtO zy`mvUvzRLgF|%Qq%ihC>l9omYE2tq%* zU;yQk0TflMGu5$!!`$@#(XYmQyRcno44K=ZXkET$wlPjZ{eSfeHU7~`nDN2)J$+`F zo3M&n>;hHhy22i;p*>g{*7c8oq;{IaI_$yLt7aTtbtivPk8_30N)=Yzv10UE+(@ehyo!i=I@0f<|;t8If zr?}vuKaUM4-jc1xyo78X4P(Pq6c%?&L4OBPrGA^Nw3s5oM3qc&IKv1KNE~%i&{znA zvkxJqdmj>T&<928LolT*n6C2A*@+Bz5_tMB7q=DM_dp>PY_!d7hiz%QY?SS>F}BYp zB5z+Ck>Tx9cBGwVGwpJAI$R9@*$&y&c81+zXW4x=d}!LW>@B-Jd*ANBzP3BEA8go= z+g-S_yK%?v&P(mhcze4C?`7BXe)!#B_vTyMefX|+KfVvrGwgx<2zwBpW9RuLcwTF7 z!57;@_#JiwUv6*7pT_T(>|y+Kdn^8(J%X>XM~WJI8_^TbLH4%Lh1*UHx3?GD**l7z z?488E_RiukdyJTg^qKZJajrdHEVg$QP4)!wfW4b|)!swAV^0*T?Mc$L_mCC#6xq?< zTlTc~kpt~Uxr4p0oPzZJ_B45rJzXAZ?=O$G50K~EGvuZAf$~cGAbAb)ZmF6W8}B?vGPazIOW^Nt19~h)fwsD_K9ksJxh(XPg1+sC#!?)Vd@yW zM4e`zuFkg4P?y+eswR83y2GBM9YpWPx?!R-~qDC|D@H$|Z?Q}9@M6D;!Au@d%{ zyi#>Xs+22vgdktcW!z!=fKVopVF3Jd1XAUED}fCB1eL+oG$>+ zHF|po%;gBbtKgrD)8e;l#1T-5CUxx^v`+1XqCxai^(Hl3^n^Enb}R#T8eQNM0A_Y# zUl`a?D_OYYxY*drXT_`hIKa~ms?l#ETvCWswd@I#%ia)uRzs(Apvi*=Irfo(zD6F( z-scnuuT~y`@E=s8PM(jE5=Pw)T4=7te`MywrXOVn{hbm zA(n%m0?aN93LY|Sa|ej^bw-5i>MWb1`r)mwYuFiTbEE>pMln9=&DtA+lhz*o2;#Lz zDqee}pj1w)3k=teW?A*eESuRwUS-@Xyh0I)Di^Wd&EU)#*V9Io+t8y4%Dg#AZE%i6 zgy@X;9hzxouxI&IKHig++b-^hTHZ(<Oz=W8m2dn-ss?+S4fp#Ws!|Z?A0zTZKXbHT7?vZNt zADiOBAW)aqjRFsM69^T-;g`J|7Ayf+QR1${UmNBZFPZ=I^$7*tRQ};EMzh> z4138aYHp%Wa3{WMp;Kf2@2>dZiPaeZfl>qL&!EOO{1E#NLuE$H%^-i4h<}7{*0=0sma?-z@Il#sMf@qsz+FOoWF%OOu$^I zbz0=L7G#gzjOG?MzmFz2*;37-<}Yg1rr(D!jr+WbLIZ9J7HUD!+bRQTZ6jg7MFT=e z`v2SEV5)x);ClF0fbuGFUke&!Y!&N|lmA|@3YX%mSqAHq<*c3kch(&Wc$?V|08M<5 z4YDCpwjX9Y;<>y12;0+slr`FqvBU8^0nb_XN}!5Qu=8#BZ2yaWX+O`_+W!LD_#*f0mv{}HUF=tQ5BpW#*M5y}h39BI zceLN&e}i3*m`|+}}_h=~F>A!skI3$)G>{aH9`I{M|Z(HrQ;hIL499P64Bm zPzjrzAEO}-Znh`_lFgogZh7?fvm_7SdfW={RmdBYv;toM5i;ZO6AE?dOEX`g83m+h zKm`aUioAp?81j(1bvSZenBGI_VFpi^r(qC7#xd1vm|ll#Je@)gf){C?_AAO-zjZl& zL#ay*Yy0uC!w&bqDBxuRpl_p@X07{BRf+kw8*Pq-lh}a9OQ^u zwe#7Q2zuGGRvB&zTi(++L1D`wznYGM#jj-KM~W?1%hcBRs^LWn)ZOaEVh|NG{zXt9 zxzR^j)IyY@THeX1g(kix=?|1*+(Ls3poX{VIp;);B|WkZtzNgZOH#KMz*rW=5LL7- zNa{NFAk9Z3@!;pPtxRJ`w#)&3{Av(I1<2n{Gy>|tQ9Y@ls6t_69iqmE{>P1*!D18< z>@6)vV{2kh5aIo;6ng3`2lo5eI7EyTDW&i|c@rxKx0#n0=6m^gc4vYxM3Nz@Pq=mJp2F`}s#*}Yi)!)H-;#B~ec8yUJdN({eq#&2DCQ;&XW1;p|yQurD3OesVPT z9h;Xq4i6od*E${_=J>iRa{*~Gu#F~TP?jNWfkaAn3yrzdl zsL+F@y%CU~k4E5&t$|ZcFv#Zec5B&A)x$%vmhD`^yIo+NFU4)rMqBfKSvz&#(}PzDk93 zoWeOmZ{kn4r3ighNd(Q%TGYG(lTJ?E1vU2rMf8M!9Xue?eV=|UW+{3AG}tFl{`5T1 zR01yy;7AsnJD4NHzY&3xewT#)HEe95^9JM6B1`?$L!Td}9nXd-;(12iSxsiC;iC~@ zt#DR=eYi3#iHkI`J!vJ#1*A!-W|v5sDfwZrMZ; zC1eq?dNKG3m{M^4M)nM0z(eR@9Ql`GB_M2A94i6L)l?h^I<-vghB9(w;=(K)IIpRrfV0BT#aJ4eVQ#V{VYl?9AWSvC*TjUI)&PqT zOz*vH8XglGN{WqHs8MX+iTP4v19Lf?Yf!md32Yl+rUQ4YFx>7Ws z5Dmw_%L`Is=w^M}lcx0jiD7Bs41@mtbZ8+BVA2`LGR`2Dbq2F`&JfnoX<%KPEm?Ow zw{nKDt(~pd_6{s7oe^v=XKU7o-v>L}u*02E>_}%jcA~RAyTI9jUF7V<<~w89B4;eS z$r;Dq#q)D#SN5GVf&JUrjcaFjUgzw=dpi?xD^21foymNRvnSuxnZjo{Q~9~hUVN^z zH^0`|hc`Km{5EG_zQWm$f9Oo$Upq7SYUe<{);UPjIR}g0cn)?B6k9tp#2DvKVh`tV zG1WOj9D?Vu&XM9|=O}TObF{b|&jrqj;s$4yxWze1-07SwmOH122c1*JQ_kt)MdwWM zx-(n6>&y`!J7d7ZOB-iGv2XOVosSu7uOu9vSlH^@)% z`$y+SrJXy}Q0Fgdgmb4F?HF(2{1n!WPWS|~W2Og21Q;jI+u=%*r_BD6xB6+j9i#-OWuqo%W z)nM`wb>j0n_Jp~;1Kyi0gSRH3O*<@ghIYU=5}4wl-NEh(5|B1q~xu(unE0h3PFV_XDeSDc+{#A#uy)@&W`B zqp_94T4T#7r8FC<4x&7RahN;Z%seoT7)}SaBM~Ge1xQN3o(7dAMzJC5z@c4_>Ea?| z^c|{T2w+(VpTPMB;{7e>yrCjhHVRsxJCS<~;_Db*7T8_%693^z%+K=Qcr8sJ1u99p zwwrE770N;BBJFGg(7Hvn!pIxOuR1rq>y~P97l~802-E~pQ`l-%7~}GdMthraVRizF zj7tGXOE)rX?9P>p!4X1`Ftnnss0*YRLKSK#g zsDs;LfVg#i_*a3|-v5RHCg>b!;$n0To+1o7CkfBXLof;i5m!E88$}2r5XaPPmqTc6 z5Jblyh;S;=lWu8XDmk*2B}p&z4ZNHex+n}C4$3uG^ate3`F^j#i=PJX5Dz5WycD_y zN>iOn^$g*~%b+?zY+evq%%amYp*U?O<-1KyE}@s92Z-U0L~9U`JYq<)ky4a+D^Iyxwwpdc?9=VOFI0Q*J} zAg4yp1a{A@Gb$AIu)0;DN_3tp56i;RWqeew#3+@N zK1JjY(ITlL@(0UJ`Fh*w~)tHa=`&8Q4)1fAW z#}byq0|ZeiJ>Z`D4NtX$?+iU-|LJc8fPx{#o|Iq^Q8U%EBoJGWM>ck85Z+e^poAgkPtvNO{C z+|F`<+eHp>yUOj{ZgPy;HZFVAsalb5@j%WK^Ja=tr2 z-rx?DZ{higo0ng^gXJ&o7P8qLqAa&TrQ9u5r8`u0aJN#O-QjAeJ3{U5ZlU&ZN2~qZ z?bU(q4(eEUM>P}aIquHt&+Zs?fjd@RIHXq^|HH% zdflC<-gYPH3U^P0IG&=b-KqLOw^1MN?yHY;_tPi4)ATv+{`!3P0DYBvC^419W^6EB z`Uw$$R7?cun-d}csh9}RHzq`YK2OC&fc|q#1dN6|Jwo_x)~I(-L=HG0{33F|g^7R0 zXmYshrr6}$gB$6O)RE9w@Ze1PEp-%9K6@4>#e~QQ>&o6JvdbG{2sIZ1h3W{2u<2(FZB#kMqZj7Dxqu zjo)dsKq~nLex5oSbwh-k__NXE$iVp9Go(b77|7Zi?TIRJ5HC0S7FFUf}eimX_~ zUWd!;a-^29S3sVShDH;J0D4<3mH}(;fK-im3GS1LFsPO~u3IhhNjbS)Qcg}Ul$#G{ z*P~G*fT?{%I;9X6=02mLk&?ggI|XTIl)yw_8b}k&fwB-;;#j20)P8)lfu$5=P6zY0 zO#PX6RL7aO=f!zx^(cEu9gn=UTFIU_?9+06IO0`MD;0)ZF>2{%kAPf>RtT7JDq0t$ zA|hof8&yQ47|NuGQu#Id9^=d~3P%RokHBD_`ykcKu-|eHfo*(G=`I%AZ!Jewvhrsqa4^=A+I%?8{BMq89FlNKL=rO4Z9)E z0>%)HCq)R~^bpGZJqpOrOtSQ#Y(jV>6j&?V;Dx1)#IE;IzO?b$=D!<9N+sk-3C^dM z!jTd<;p98#WE+m4$|g6259TX@TQ%_k@L618Tqu=8{k<_v9hlF=-DV;vpc8%#J1&AG zBQd44Ga&Q8+?dHF*+4YQSx_p!nxWHwdxo+tGla_V429IssRg;rLn7)p9;VI*CE390!;hEVf5vKH_4I^gz&6sre`t>j++_+JV8l=c~5@E2INc3vtPb6B8$Ob@1A&60_6!e zSCC{Fs~qe@@{1Ia#UPdw<$Llxwi+}`85R*muni4a47PjgjPismE-F-k&Y^-|8iFz( zl?x0#7=VnW43Nc?A!xsZL>7l(8Ii?7Vo}hL$z@`)SS4gJFs)12JI#EjX|yXbTQ#-pbh_r`ad6*oXhGh%COd38X2$NXWCh)B$(XwnF;peG*& zeW)cvhz5Dzkmpe;kgE`-fi5SaHbq2jU`nO(5y2goGPOgS$Wxc! z284b-g=6-}!ZBMoMARG@<(aaD@3o2TfU;XBglP~uM3~|c6Q(B{!t~6DFg0g&)ChH7 zXrvb;OPHCZ>g64WH&-%P)7Y z+a3`3-?B$+}lK{d%MWEcZfRoPSL@=TWsO} zRc!6vBgVV;irw9%qS0L@PIQ-x)7`&`Iqu&keMMgD zzAA5YUz2yZZ^)(YoAN>TZTX`6j(pdBSAL1~Pwq!@z5B5$aX(e<-Otqk_e(Y0{aTH3 zzft4eZ$TY?uO_)asJ-1E)&A~J>PYwBYL@%6I>r4(&34zS^W1gnVt2h-i0AEYv-+FI z)uWzJPr;+!tDaIHBK@6bskNT1Hh7L!o~zTIr?Z}~Yw^2>SEBoSrFtly+j(idlUJ@M zdKG#qIH~)28GWQzrB6osT*O12>s9MZy&ApHtJO_joxT;nmwO%b3a_Jn8qb%!&iXa? z2mOKjivGsyroZ>P>wkNj>9t-Dy}|2gDX*91d%dl4ua6aaeXZVJKWkg$@8S)%cJsEd z_Vk8WGrR`t5TuXyhFT|j!>rT1t*p7;aO+~Euk*IH?(#-j_j=n{k9ec3mEN}2DsMaM z8*jAry|=yfledFC(A&}8!rR#%?v1gx^Tyh{dgJZM-mZq?J%f!POfjK&k%}o^`(UHJ z4mAq<;Dq9}XBawHL3uZFRHoT5ySJfkHEdM6MH=#Kl3i}-FPrUc2ZnBT5cj=Q%?0J; zQaBxT0a6}2Pkf~=M9OCyU?+;WzP>>JpQ855G%J2F_o? zm$RMKrN}Ggs~~#245>2yI%u`akxGkFgb^hNWThg*#~BA?WugP$R$XDo z=GCT^j2MqlP1H)2I5D=i1y_WfY1+$55$EOP8S+y@rMvQc`L?0bJ$bd*BNDgC8^tg~ zkq7b)F({(f<-NC4T$OR3Nf!F)z^ct$-M)8QFVtq~nwrPjuDc$L}^)8SdoWBNL$(=mNrt^3CGbx!XS z*UjmR3gs3S$~~Brvp5@XC}`jES%aaB18X;)G4!<$=R*;F9atx_Zw-ANSSQDMDH#4Q zG4yrH(9%Xt(d<6Z(%?y8oniM-60M}V5@Bb}+=Bj#=N73bB7A!`k)&95Lou3_Ff zIOAyf)r{{a^V^Li{efb|7qhXl;CTJQ92gvJV4Y(EyBSJx%C0PBh8(H3xM}}73>zF0*ywz ziOlgPv0863>w)J0Z_gqHy+O&%0+oX3W^rJYLypA|wE(9)Ik}7!^dh4MDn;D1s1*E1 z7G)M7=jD`HAB?_n&~3RZ`ZUERD#SzqsQHp*MccE)oL?va`JB*u~zz*cIOMY=QRzyWV?=-QvB>?!mhUy;sdb8#6ZcP+_4T zQsmc^R^lOSC|YO-8Z2ORV?yiLc4o;5S$3eENhXYEu|9F$#jJCjNBZZH*AHrqHbm4K zZ3xqvx*4rO2ZUWiaV84>SjH^c3eu74Vm|N6I!s zm=h5*EMlp|bcv!UsH^iTVH1J40~aElgZXsK05WQ*B!&MPub_DQ3WqJw_aZd zLnk0sAZP%wM8ClKG6GmKXg5E>dO|Xiw+4%vAIF#n6ll7;G3ME1L4?6psCz)yZDa=c zT#PHHBOmb>s_WUY$*6)H4deZZ2CnY)J;A{MmyQ51ru7jdbXdKC_r zs8vv^@cY%O4oWX=Q~KQ|zAw?feE0j*^2y88-y2f*sr!(40E(9PunQkh4>m-y+yjRm8zaWWQ0BcavB1*#c^PG&RwX#$WSd0{zXUC&X%&rR3g%dM-CT zlbU7J%%?eWAVAqna&N!l4{YlJ^;#;(f{X_P%EOdf%{vy>Hpk z-uG;#_X9h{Tg_&BKeDU6pKv<=o85%;-QLe^x%UfB=rwGWx0byGwH0> z)ObH%^8voax9}Z4!gu)&zQ@P=KHtL+_%VKpACL6uei=W@PxJHr3}5V5@f(o-i=X3n z`_+88U&B}UwS1M|j=$iy=dbx4_%D7(zQOM#Qhs}p^Sg=;emBwC?=E`yn~9-*4>1Di zo&BC-qTfqQ^?Qr`{669kzppsT?#DXMc>W_s7aU{x~_nA1}A? zcadZKUFGin1UU`q!~EUl5&j2t~Fb|XWBOV=j`yK(( zkBCo;O_B4}`>0{Dz2!CP1LVQEgS<+8h?E20+!v~k@XlpN%d-%-&cgk9oZLY@g||MN zjR<9A3r9Afr69pR$8B~WG*3T4UJCYtk)~w{dkN9ZzQo&7z5`+ze~MNDJ^}YFL6G9- zKszx?-HRA^pHX{!1!59^4ukR(U&V*MQj%T6vjmgScrRwnb<55A}^n zb%P5s%1ev=*jD08^(|5mlA647aXsr1zkQWuqZIT+FXH5PEs$eCV;Sh>@vwF_u*kVk z-lV(|xF_fnnbH~(aVtQNxs4G-Gov68bv#o}GE>O&;?a`-?Wm2xm@{At!e;%Qt{Y&P zhdZAm=ven6-AT$KZBU>Jq~X|%Te?MavenC&1cD3gFu}#6nl1H-cFX_5tO4ucp&IRV z5a}$4Ez6J#B#>><@NC_*t(M#BK3jcfK&bvB<-o@Jjytd=;%~L!C#D%g+5Ul^{7tvO zNu$;Qr3)XrQfnxxv`v;_8|<^g7aeuEW1A%n(%{RSs~k}(uvxBTBEPKyRkQV2wc zA#|5U?Zbl0Peu)MCaS5XM>7I(Y0Hctcnaj2rb5&b#NHhCOC@n`jNF>sGLp7Ejd;3v zJUbW?GWr!pzp8SjE7`eZ?G(T!sUe54O~~a8xp)kffCuFv$eV~fP z2tma-epM8MZd;UaS8S#}G%UQ^y|@`n66H-U&ZDL=_+J%;G_Y;aqRG7twuVFjspnc| zg1#^4u!ceOiGd(QWMeXt#KBS$2DB>4Cl=sqFC03_$Ak(+pBVIP%_oL}MY(B&XDYAI z@RcVc(KA~@=TwHysVevZ4C9jt^4NSUH+6|?y776E7bkA&s=~@#*8Ez^Rb#%g;I(cg zG>xm^JIBO4DvzXuvGzt4QKq6RC{w6_8$}K>lZ%2TB_%aDIVPM^xslFdB5{ySrbBMP z)zH-^s^l7|$MRc93MslaK&f@-uw)+0DKY4glqwBysM}D5Uza~& zhD_(f<=9uWftKoW64lRV9U{E+HOLH_|J5KV zv-Kb|zd1-K=$k>}`J02Jc3TEXhN*1^Nnb)?kiA1HPh*d>8}kN?T(<3iv*yGajCguD zxCYnYFvJ5<$LT@t;IBi-_?{KI3h04B8)%;_E8fQ$EbrOTmd?}^k~zdcKQqcj4LOeX zx=~u{Z7%WJnnhmDazc#5;lY1h&odzPxSrz+2GtV;W~ zRmz^gSE9j_a>!(NSqp4cg(N=t1jbytrFgnk@oYg*!ljc><<4l_ILkuwi4;*koUTOCVS7H%|7+#u&?p_*`LLg ze>V60V|m6ukGJ#B=iU4Zcpv{lp7$^2L;OqlaQ{-iqklOc<6i+9{YpN~zltB=U(HYT z=kYWAYx!0Fb^H#00l(K@$nWi({!+1rf1jA*-!Jy}9}q|S4~m)o zL*iWjVR4PWLfq~@0{Z+>aj*ZF_?!Q@xZhtX9`c_MkN8iD$Ni_o%l<0yiT||t#eYVu z_x~Ze|4*s?XQksmCw>24vc!Kuru`RXrT>bo@n4ml{MTg<{|(vCzeEo8-;pEzcjd1B zdvcQhzMSHJAos@eX#Ydd>L1Cg{Ey}J{wMNw|5N!l|1kyav${Rt>dru@zXnpR z43v5*(CYobQXd75S{pz)An*~NFVNLNiLMLEbmt(gHw(&j@1R0&A5`kyf>1XG8GSf@ z&kVBqG;knq32O8Tl>J9gr(X)%=`Vuz`p2MyUK4b*te}(S2c4~sL04;Fu$eVD=wUSk zJ+0wEFKblL$J!z2YfT9HS^ET=TSo-_tuuoG)|J6P>)v3HwLHjM4+Mj)XM!!Pw}K(o z7r~ac6%4igV3=JVY-RTd;K(r;X>Sp1V~+|(*}DhZ+6M;P*(U{~?RmlW_I1II_TpeC z`>tST`;lOb{ai54{vsG}e;e%L$Y58eI+*Bm3?@071(Th=!JbY&nCc7*_HsrAdpom& zeVm_zMz?#guX|9iw|l6+!95|EW)ul-0Y@LiOF|I2Gj$F8<$w-E9tnQH7jb7A(nx|> z>TU$)3TiP!b*-)g;Wfe?qTzfG^!zsL1>GJgi|xYxW;g*h+l?*N9l$Gb*a`6JLn`qu zJB^)S1kE0s!;TiD4B)eK*e;MI5MShuSj3)UAHn6I$>Z&uDY_$n0H%N4oC&%UQa117 z?4&y*<-ky58{Gvdmk)79Xo_C%@uBdLO|bZUM+oYPD-rN9`~%$$Z&Q3cf7d`&!mq;r zC{jWxzk{E_ovNssd1VdH&&wF?PiTa^mMgZqm1s&E;DOXrMG9@jrl;<8Y~BC za_|^h4YI%Pg;d5GE_z?8mBL~STy5xPM+t@jJIzg&CT8+ua(Xi^}Et!!HrglGuwP{Bx?Iv3a)8!yBvNfHk>Nr`BN zgU^6iImIQT5bM!j0%8N}W;&=TAAu*lP2n%4nh~PF$hbFM+0?@y{8RXkqM;C11oG=0)WOc16@8TIk-tz**Zg5a^O5<(nQx#qSM+7mWkG;vdZm zX?Pe#flYV4v5Ltu0rZAQJ>x&FkN5%^Ok2B=fJC{e5uBHsWQzV`kDU2m!4-`Rf1zS1 z>b)^gH0ZL99Zh}2KM{wm2YkZ)=6>1ekfWw)3(&M3^1rqrXutBAB#!+bp^;HbfR9+E z|6^nSf558vKLbnsuPqhN*8jW_3bXZp3fATkL-#j6DezE*-dy(w$^dRC#QGHX0)gg0 z*IWrMLe2H8TbvvQ5}1;;XD`M&hOZDKtpn=FMo>5c@4q|?A zFv|poux@zv2@We_r~)qn2!V_M2fVLa6+>u^(f3RZe8}Iu#h5*@Ik#T+&U}hsNAEsLrTMh>2`yaP>Ea2 zy>7FPtjXN#MIOqiT{If`^y)|^UJcBR*zgoQO;>EiZ ztR}E}wa8l{Z`YFVuBoI99xbB_(J_`0z7{aM6w?W|P2|R_e`sC9x|)eq5xHFkE$Wk? zU~h%?GQE97Z8j*Bwx~wVYtY8?7aWW(`Mqi_>o5{ZCM-CQso;EO2XnC=7qC=tA*%?W zFdJOVx(1i9zQLs|A6(A146eYMT*>wgu44NK^VmTscT{k#Sr>|O%Mfcg5nWtbSeHu+ z>vCzlE{a_UWi9f{0&kiI3)EPvbBy6De8{o0c!1Th0oi^=dgtKRP6oSkVH7yZ==FF8 z--;Bj&{tut;E#@EY(;Bzh`G>FU_I*;En7DmGBYe0A}<#X1{aIPSpSk8YY<(&N6p#w zHOutQ^)>e$rK{eWB!4q(i@+7l@r&umn#p;5mS-;>J zGeePYx&DP2>d#kVT(ooh8{<_9N!AbZf{-*u2MNJF;a@~T+5)fmKhy-VSA+m%i?dnhXbF3e;VG;K)E zWYN-D{+|Y}aJHW-pTjQm2L0q&EMKe?hMN5i`&h~id zMZMiZ`X7i+lV}e`UQw_}DUmNlpDchnO!3TH*e8~_1RR( zmGfaN_I$t|x-i7<2Q|EsQ;KycRPt(L)Q{ zFq5IH&G7Csl`Xd%xPwVV_Xn*xZcDp#xThgwJ`Gk&tlRF02+Wa&rs z_+@%34L=fH>P71xFR%`Alx~Hi1?oJ(+pIizmxaN5xG>)b!tntc5PZmn1|PHG!6$5c z_gZ;f0APCs}yJJQi7MKB=3?^ygSmHr!?OpW%1D|o9~r!_~9v+ABFTu zDUY9*^7)mi6u&)H!XHhQ@~2Z}{Ml5RzmO{DucRvY8>veE4(flFV*H0xHD8me5vf$I zs7cj{9;tR>`&0+9N2;TkmFh0uOl>BEV2#YAdeTY64`yBHm`F|{S*7=hE`8)tPt@^nQO*l z)5JaI0(02`Y^r(dv8TjXsl3edup_mbG!DA|xNh0TGkb;kAeHvGCjf|g6! zdx$k*JOHo{#b$bcc#VmHQJ`Aa9@h0o1|AZ^K)?BORs-jso*DB5Yt?*JHqFI3|j2q2> zrb{0RF#Kp%OAhh92tkUqCv-p3vPTOrE62SH8rEOKO4gHZ%M4x;R+bBx#Z$T&=vl=^ zs?t09zl``Dh6Xo0h)9_Rcv#d$>vAz;qyuTshQHftLfaQ?2aLAPM%ztjd9-}!A8gs^ z<}`!P2sR20g&^HH*n~_nHVR?Q5pZ)9LNZ^HD64F=4#*nZs7LCf3ODM}hF+k^qJ$ot zms~_qM8dL`fNN2F!6nV#;$$>7{Cgxe{6tc~(y<#w1jjWSKMFUMN+Kpql^>PH(i|r*EiBFPv^268AOfn*$Hq%D z7HXR?%!Vm*BF~O>NAil^^dsM2^GQuX(;WsJAKEpza5XMxV|fklZ{`Plu^C02;&*OO zva#+7^Z-a8OswOd@DPWee3Ze39!|_|4~Iev#DqFg<8{WHxkeScAV|VSwoYWrJT@zV# zY7%xAtVdFNvOcLPZ1dD!*lGK)?Nj@*38`spdg=gnU}^?CI&~mBK6MZ~BXuZt-eK&# z)KTo>)Y0s+)G=&f>R5JH>IAklHIuDO&0oPG;|?PGO&=PG#SwPG>))&R}a& zM{zrK4o{`ddsH9C?V?$t~kw$7U1QT73d;4Dtl@x$(3EYe@El`37JO z39S(*eBqqFfXy^md%!MZ2k4o|OR<+(7eS8uOW6Ck4T$I}^+q}q@t^?hz z1aWjH5XAU?Vu+E7h?ebGipVUSbBOkYlYNiYQBvsiBkL`AoXMb_^%yS-w&zt~esc;^ zB}(gXXcEnHq9`eRL9^%!QnzE=C4ga7@&y$ia*=&P6MG4iLlb*6rmtam;F1&U31-Xx zzw+KYys9euA71;^TTi>WH#x~oLa~MbiW(#+gS~GGc5p{HI<5i3VBk zWxVrY@@?*CLMmCQPy6^Ux?Ch7BE~lhh#4Y>B$MBi#6B275WiT^NMoX^rFa-^2f*nN zuvAgc#(*=pwPd6aIN~IzPa=75fclG{Y7rA_kO!+m`no%zS)U@W@x1CATI;0>1MD02 zV99V+2hz`h*iS4JbRNi7n4yoT)OP-4W!|YQA_VrqL|*GZ=(XF@*+N)aHF^-+u$ zwu$(++N@8jPxK7@rJ%z7!g|_rH0c)y%m_cRBzUPWlP~pxRp?Ppzc_jYzaXby7KN{n ztw8Uc$R@v~uWGdIPwiGO8I}{ zX*wwT3>}$$md0kEr}6kb8S&?3m(T^-7wPisU+CKGQo29;63xf=Dg0iV{VTnaT}B@O z*4KdfefD))k$pob*}sd;vu}!C*>}Wt*>|y8{~-os-xGtf{}e}L-xtSaKM>=yABu+& z|3da-c_z&T1&0|Jui(E#5}M*KQaqd2!!zLucEw|oItBYlG)d|d9(Khek~%eD@8{i6 zJi80MB%jO@)QcXLbX$_@XkYo*l>+flkf&H0Pm~AgS6~gKz&_bYrl#>EmXN6#(Vwo6 zYu*$`&@lZ<{VMDT-+~m7v>Y%FHQ*RK&~hpsNq`+fS^yWTF(qlay$DK4m64898`Z*e z94n`SrfR{$hTYtNrfJK8P)kUC@y508^tO{Tc6#(H5Hiu$v35E(&2&gcG9I8OIY-A_ z91$GG(85{ z*x;(z!#nE%f@;ID4%fA2uzQVx%U2}Ho#vlX)HT?&o@H)P9`=_)2cXpA!?8PDkKlzh zyd`t8-_GMnkSys$@FTBj@7x#8giu_cfdiSnEG?!#V(lx#^7J)=!I+Lr9D!RM2a{^=_-YM1^ZWxWf zj;>EEA$G~G7W-xYBZgZUG0Mt{6D>Qa zA_Og0@RO^t$W)x0-Q=F3QLU&J?;}o!i;CUk{$S7`%xU&)Xwp+ORpDoQgI=OjMHl5x z><^#NXyqX(oNyvl>J8X4+G)+oT3Nze)++CRdtyJin7}B2v$wHg4eNB{F`5@)$ZzFn z85gWQymhzJp^uWVe!IdAULx9KqC!ELRdfZH(%NoJOn$>Bd>j$v1GT5$21|#*%gzAw z3A6rcT=MI87NlvYC#r=iAw$dhCx^$NS@0R;Gr^G#HT+ zTJ(8+6E(GkL}j!1rf;f{N`i%YY*aX~XtaAi%Zz2HF7U?2J|?o-t4laOZ zl>(4571{vcMMLnB!E*trddjbSAgNlQ8yZlX(e`+PueaTiYt>Vo zwG-`Z4WK=(KhXZxKswOcg+^Pu(oxoKbf&cjU5fab)}C|=%G_h^BYP}(?1>!)(cI`B z17|-^%l!#G1WJQ%<_PHzODZHuw@b=MNAI4mOh7(^YUz*S-{3$cK=B-}KY|cbAtUui zk(Qx-U?D}SZ%Ilfa5{#%l#_S{a$Vv@jE{tixCm|$Wdj&Rz~DXW2r}6ef1BPmZXtLU z3Kjlaq2wloAnl{@1N*VE5Bxsg5IJ$&f!M7rLAx&YW_uzf{sUE37)z8UDpF2 z?^L*X=^Y!)&W#u z4WV7F18I;ol*U=Z=w$04nqm#7bFD)#B}dS;)<~LT9ZL6Gqv-|fFj{IIPVZSq&==M* z^tE-IFs-p7XN`k7{_%3k21jZ!+W19Le;kJ)rjaO0>j2oAL{Uy&Oj(3@@`58Lips;7 zlz1ByMd(nbcr!rRClG=e2bBF9A-qT5C$02uXq*>|t{?O)0hElPbo=+h;5!_sC$ zHO$BDbGegg&_+PG-#1lkWjy`0eS8y(h_|)+q;ER%j*2i}u`%fm#1k8<5$qI(c?FH- zy%!hmDnGn%^O~x~!r_eYhLwg*MNSH+VQ`#eS!|~HZQV!4v zL+K#Ixh?c|08#D^HV>;RFszMh?rm2S!~$do*4fdq;s|KzXVu<)VPX;T1W=>z-AJZy zX}$Zx$|9aJ!$E-;ChmMbrc|iD_>LhTr@Y{CYNVboOdrB0OaUGTrXShYE8zW&T{7aa z(#RJkBdP=*6ll58KsyyY-;K^ZHQAOM(cz$?4z`8Z?q>*LFvb&GqZ}QM~cu|fn7`Aukee_s@<=$ ztkRmv14#FjRoXC0(Y?km!fS)_L49=yrW#{8GXes{*Hg>#9cjc*tymE^EHQ#j>mDU7!=Iuf?cr~@Y z-mE{*4VGEJFP-)CeX=>tYF4V;9S8#kaorW%I~8%=<+VOuA~D^A-hn*>G%Fi4s~Gi8 zC2B0@$l6$ABtSauEs<3w|gWl;Pdnf640hlf!815avzOt+;=$$TF9c$=B>ow?| zcH_4h_s(W9hTZ+{+$Z?6%!dQNrTi!Eoo?Ja$Q$-fH`zN9PxMZgw%$R5gWhRebyYsW zqENiY7`LoCUj{%<8I7w>4X=SYwaCmW7zPQI=o(jz;(O<4@W&v76K#x`t$+5DF`fx* zj99w7>%jR5#BERkObdrJ8jf~E$j#`$crzM{13v9%!Zn5q=ONNLN|m3MqCAIiVqO|K z0VRi2=^%xVeNb}$M5YwveGZBDult}sjDuHS!br*19uqZ4f05aM($a|zWtvb8rIETa zCEBDM5#>{kLB1YwhKF$e4L3|9rHlzqVa0trgoCQ)~5G6=*|K^#t| z;ntZn(mIQdwx-aZtaES@pGVhQ=hG}}D&1s3O3Ru?E!K2;*1C|ESr^l5)+O{$>rz@{ zU4|3+a*?vG5LxR|QEFW!%B`!#7S=UlPwQH7fOVZ1YF#f5#_uDnnc{fs&*B7Ywm8YU zQOve(5-(Uci#M$~;sfgz@h|HR@sri42jm{7>qT{h^^$s|^|E@5wM;$PdPTk3dR4vGdQH9G z`kVS7em`!#p+05(UHzN2Kz+}8Tm8s-NB!D*SIb!c(DK%M+E&*4+P2mQ+Ah|I+TPYj z+J4r*w4v6=+Gy(&?Ii0{ZIbnwcAE9McCPiMHr4t{yVCkvd%*g)_JZ||w$%Dodl|pq zvcA*av;M1n2e_-O@3l474|?AEQ7^Mr=-XJU^zE#l^&PF%dVgz;KHO^6kAleC3AWHr zwH5s`Th(XVntrRT>v!9R-h%M+c0&Iv(%!UF`uiyJg`F`>J8NWY%W!Sm*vfX9dJyWrN%}~oa>JJNkw)DOLsCUB=m<6h>p2)8?H^FaDaRvp zy>^ql1ekP-c8$uK5ea%$+fkVV$VpnJ)hf3jgvWMmYyGcCg+WKMCDoP@K0&TWW@{WcIuW0S>h*Em6V*V!1TJhHEMMfP5Duoijb?Ml<(9UP`pug-LCFb zsSco=`T**!R*$4fl5VV4kD-(FWx%wndNp;?UqK&qQ@<9r+7o4cEfYd6*R~2F7qx9e z$d%eIA>=A;bO^bNc5Vo{iv|;~5^^`~fe>U{W98B_I*abIkM_uWT>c2R8B-rW*bX-nMW6NXgIo{SHsZ-y&8@#QuS~o zi)049EMAOzxd>lvvZoMN4BCy5AG8sn$^biD-clh>;gwCFPdl)}g1oZvU2XsPe-O=! zVY{ytcSnJ}K9SYNjG zFeMj!{h67ec3Pln`GHmpmXvzf000{Ms}++` zHq*(nD)^gQg!&B~#>!N-Y}eK3@&))G972h>CRkVjW7&$w4(YUEgOJA694!3B^6yw& zskVR~SP$k*u47897l{sV&>0e^9&GI+&=J_67Of2ojVs%jo=JxKDjS3ZcWJl4d&Y)# zY0MMZmx`U~N{s9}My1QfgX%CakQ>_YQSOKu;K{M^I|VA5xXFO?z0O_+yRd-A>{4)! z3W03YFS3P`jV<8FXRKdlh`MtubYP-NyZp))#H@F@A~>^LWQ);A?3C_?KzDx&*P6g8 zVMkms9?FN)X7na_t#O9O1UEgW#s-diJRf z0~;ZEjTfaQR2JP%5Z?uhcunSbQ zOR2kEM%&sx)!F6r2fKpyvny#B(hjq`&{(@WooH_^c}0Pa!ATLdgLC%7fZ9RmBZQcZ zeUkn!gjAfC$LqLV;5;l+U;Sg8xH@QoZKM{Vi36;g&i1wvG#3AuQVN8TSePOxnLx=P z7v4jHT=Gn1w&e)ImgdB#96Fr)JYJ!pMnVuRQ^N5Na$$Cxzfuv7WIqTV^d<1=a8{uULe{%gS>VC-sG~&A^Y&TuZMT2NZX|r*Babv zTVLiteD?*L=7vb719K}~Fq1$Q22b1Z6!?ZGITl@f9U;m50IMNcFjL3&H4Dkqk*w*$ zKM^P-ap6LXZ=?zt)IYoaW_YYtg`X}!Yvaqt)-;+x_THyPod0`_p~) zPV}g~GrejLpttNl(5Lo5`qtisR@=J@)!t1cZK%K6dkD|oM{H&9D|*=bi9YuJVh4M$ z*v~#d47P`egY5&w2zzI7lzp%`)*dd#+lPpYY}gsMM~bWLQQ|Ipv|M7-f+-g*G0_-a zC=%ja{U>>$sHWW{0juKju(`T37Mo5d3SVCV@>ZpDMM7R9G$?1kElF7&5T2Cz47>n6 zE?*T)$X_(;D@6nLp;zf52`>%V*$PSMwpr5(YK7mU5Lyv+@dAYEb^P?miz>DOi^sNh z#|Ce@EFMwB`eX~RZE4Yt4S;ceY?L5TBMNo9K*~Wv=M>(yp@bPzMsQTtp=6XEEOorY zb>pLc-Fc$gq1(~{1`eiHNK`99q};hHuq9lmUxXe2gB#&hlA{cV%>YiulN;jmDcZO7 zNF-~Y8rY0Co7EF|Q^De@jkqe1U4ZB!w)8|KyTIIM{t;Q)j5r;yL;^&RnR*m&82Zn= zbg{-^u}g1ddtHWFA6n}In>Rv%4GA@h3!H|#1-JbpcCp!Alq01$5Yd!r+T-c^&Qw|^5IeTkTEdelaaxZW7--i z)k@&b#}3p=fNqJ8{{m=pfRE>wFlvGB1>n5}F0^bZ%b&t04w#8G@NtvVnhm4b0FMhB zHH?|StSP#I;i5S9Nrkmi{QRS0f2o1PaG`txxFsTAl4rPWpwu={lBtCw<>ZDnX9jki z8VUYW;oUc!5imx(S$khZ=s)l@P>gGpBB=8rjT|{5dP}-FmGD{@u<5r3cw>RD6}-`i z2lK}CzA%^TCi{9x$?9%Y*#ywZkdb8_qeb<8qw>GVniGaRrZM0eu%b~J1-nmhayuqp z2rNa7t#4zI!Uryl+ep#D>PG7CSI3hBuP1)lUDU;|YMUIn;72eyaFnI}O2lx-qMV!* zi+t#DF0ZS~SIrT(f$>_JujmywczWMzzf78TdfqRWOnLsfG<7UE^L}M;7P3MNN9cuW z$)pEQx{(Hlkn&yI1b7)(2&6LKwVmdS1eWg_4DMRcrgathuKB8l)`ud9RlfqUYAuLB zbXgl#jr3Doc^lqbbH{wY6Z;s~5chUi0VRh z!N!YhU7@O)60#R)Vg!P*Ag4efXT2aXtWu}JK8+|{R za;uZlGnU@)^S2Q5zbd3bdgSr+!doe*xDHmGumeQ8Ol=xYOrp1GLGex)=kgO$LMzmk z3T3yXhEyoK6{Hm4=6L$CT*|=nTF9wpCi?G11R`Md1qlWFF4nwMuL_`Ens@y|pi>pT zVF@Tz#o`a*pL9A1rK3o-kESyF80v;^tv!}@u*cDk_VF~p9#5m}33R-D0-cWV`G~v1 zJ{d&PDRiqniSDu|(>?a7G}k_j=GkXZi+v_7w9lp`_Br&5eI9*cpD)OsD!SSih+2EP z*dD(J*yoA8?Tf_$_GKWBE*D4ISBQ!Bm0~h}pJQJwF0ro>*W1^Lo9*kx?e+{Y&%Qw{ zuxEOS~A%wDFBvR_e;uwPXt*{`W*+J94Tw_jK9 zvHz|GDQX8%iDWq+cr zu|L%v`!ju8`wP9ly-457{z@Ngf1@8{f2$vD|3{x}|5u-4|4%>9Uantce-F;}5BfZ$ zFR&Nri|xntW%ked-|W@K5q7I_wIhsKj$+*CXiQitJ5fI-SR%p_p+IyFamqvpxZ&<* zJSjzWApm7OZrC6v1J^_BG(x8xjEfDJ-A8CgW4giCkPVPRL!~&K2`Pn2DNdK5*+zE* zDqJ9!ZZ#^!B5-{ZbeB;I#VXkPp?i!HC55snnrpaH#6C^)3`>gGXQ;){jU3>@y0QrD z4p|V`pKRc!3Z=1E!~`RckOT3&ky6*$#Vvk&S#M4pKwnE009Oo!z#j|8c;ZOd0AeA( zoR|pPLtPM80#bL5z9*n~0>aZWUrx+}G8yM95%1C%^={3|eKtsSl)0L#90!uxR_1AzQHp%7^01aR$`I!%^RCAt&f>guI|- z;BA)&?LpbFJxHw#a5AbP9fAvn_Bz2bOq+SlWoALH7r`YIdw<*A^}B zA6qe`NW`fv4QzmZk!@rMG6#_O_7L!5gWt>wiyhGZYA(KeP|SWPkCotl4Qh}-ys1bW z*-&w$l=pi|wMmO(cJg%v82p}cZ8x7tv2gvRqWpze5pn(bc__LvyE#(q`;F|t8zs6x zp?^Ek1(z=<+WbaqDQ_Vd5B$j_?_Q?3M5ui85>CWEyTf`*N~g}PoXd5 zU+s9A6*@gpCzeNkn}4%J$gh%}6Pp`a&k#p7wUrB|SX6WGfMeeY2<<1D33N4hK4LAf+W9vxP*b44f zFx0T8pNj8SY#>Fv)>B@(p(@CiEKOdzyZT6quDMBY2q2aOjR&w0AZ>!H7C^|WR+H4H z3a*Sd!P$$L7$E@BE$|!Ipb7;4r?7a(@;SIeCFD&-9|bWqaQRx<@`T!(5x5D$R4El9 zSczti1LQfi-Vz(WXQ#o!)HtcO#pu>zY~d##6fyBhQkTFUqv9E(aFo5~%B zx;QS?J01;lax}&%q2rta9q*LVDTqG{=@&UZUG7xU45x~2bE@fHrwh$@y3zut8!d6V z)633g^oFxJedugKUpQORf1R!9dnb^(zEa97{}>TO^rJk0G0*77kRa0OzmkAa@x=bM zBw*NJ&c6)s`El%((g*q&Y*;v|={=n_>rJQ!jh9Du0@gwL%HugnGw3Lz2XZB7J`IG9 z(N&mh3#cFTW}d`>`UW;X9ig;5Vp$Sg)&Mm0pd|DQ5|Zck7fP~uu0Wxn1zSjPQ1M(2 z4nQ~}m&@xHY4HiO?(}GhV-=Xc0V1=W>JM0VS>A{wuDo%V183<+-KdSE(YO5;wN^u$ zWB@K+IiqI>jUSDDFa;u-dSm)daNDG}F?NnF1I_AJW9kgd;7ORFU}ms+6j+J|8;2i9 zCIz;H_zL�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 diff --git a/software/LepAnchor/Proximity.class b/software/LepAnchor/Proximity.class index 01baebf302678f8f50bcf499a3cf63c51744978a..d7b246037cca27fca814772bb275536a4159f905 100644 GIT binary patch delta 160 zcmdnn#JIJIaYM2w23AJpUkrSI7`Q+*{~rcVMzvoIf|C!LDKQF8erjd|B$dsL7zH10y?wB|C!%kP-nS zI|c@({|qt=T$2~biEZwc&*5aO+x$zzmY3Oq!DF(OnZA%T0|!`JBqJCA)laT6)0F08 w`oqA=sQin8?+*hPi01#p;K`^4= end) { + #print "error" + #exit + return 0 + } + return gapOpen + gapPenalty * (end - start) +} + +function max(a, b) { + if (a >= b) + return a + return b +} +function min(a, b) { + if (a < b) + return a + return b +} + + +function processData(i,j,k,n,gl1,gl2,gp1,gp2,start1,start2,end1,end2,bj,starts,cstarts,startsi,other,cother,best,prev,deleted,ret,mydata,s,cigar,score,trim) +{ + for (i = 1; i <= lines; ++i) { + $0=data[i] + starts[i] = $3 + startsi[$3, ++cstarts[$3]]=i + } + n = asort(starts) + + #sort data based on query start + #and separate by target + for (i = 1; i <= lines; ++i) { + start = starts[i] + $0 = data[startsi[start, cstarts[start]]] + s = 0 + for (j = 13; j <= NF; ++j) { + if ($j ~ /^AS:i:/) { + s = substr($j, 6)+0 + break + } + } + if (s <= 0) #do not store chains with score <= 0 + continue + --cstarts[start] + other[$6,$5] + mydata[$6,$5,++cother[$6,$5]] = $0 + } + + for (o in other) { + if (cother[o] == 1) + print mydata[o,1] + else { # find max score non-overlapping chain... + first = 1 + while (cother[o] > 1) { + for (j = 1; j <= cother[o]; ++j) { + $0 = mydata[o,j] + if (first) + print + for (i = 13; i <= NF; ++i) { + if ($i ~ /^AS:i:/) + score[j] = substr($i, 6)+0 + if ($i ~ /^cg:Z:/) + cigar[j] = substr($i, 6) + } + best[j] = score[j] + prev[j] = j + trim[j] = 0 #trim cigar if needed + } + first = 0 + + for (j = 1; j < cother[o]; ++j) { + $0 = mydata[o,j] + end1 = $4 + if ($5 == "+") { + end2 = $9 + for (k = j + 1; k <= cother[o]; ++k) { + $0 = mydata[o,k] + #($5 == "+"), both alignments are in the same orientation + start1 = $3 + start2 = $8 + #if (start1 >= end1 && start2 >= end2) { #non-overlapping, increasing + ov = max(end1 - start1, end2 - start2) #overlap + if (ov <= 0 || ov <= maxTrim1(cigar[k])) { + if (ov <= 0) { + gp1 = gapScore(start1, end1) + gp2 = gapScore(start2, end2) + } + else { + gp1 = gapScore(start1 + ov, end1) + gp2 = gapScore(start2 + ov, end2) - score[k] * ov / min($9 - $8, $4 - $3) + } + if (best[j] + gp1 <= 0) + break + nb = best[j] + score[k] + gp1 + gp2 + if (nb > best[k]) { + best[k] = nb + prev[k] = j + trim[k] = ov + } + } + } + } else { #"-" orientation + end2 = $8 + for (k = j + 1; k <= cother[o]; ++k) { + $0 = mydata[o,k] + #($5 == "-"), both alignments are in the same orientation + start1 = $3 + start2 = $9 + #if (start1 >= end1 && start2 <= end2) { #non-overlapping, increasing (decreasing orientation -) + ov = max(end1 - start1, start2 - end2) #overlap + if (ov <= 0 || ov <= maxTrim1(cigar[k])) { + if (ov <= 0) { + gp1 = gapScore(start1, end1) + gp2 = gapScore(end2, start2) + } + else { + gp1 = gapScore(start1 + ov, end1) + gp2 = gapScore(end2, start2 - ov) - score[k] * ov / min($9 - $8, $4 - $3) + } + if (best[j] + gp1 <= 0) + break + nb = best[j] + score[k] + gp1 + gp2 + if (nb > best[k]) { + best[k] = nb + prev[k] = j + trim[k] = ov + } + } + } + } + } + while (1) { + bj = 1 + for (j = 2; j <= cother[o]; ++j) { + if ((best[j] > best[bj] && !(j in deleted)) || (bj in deleted)) + bj = j + } + n = 1 + ret[1] = bj; + while (prev[bj] != bj) { + bj = prev[bj] + ret[++n] = bj + } + s = 0 + for (i = 1; i <= n; ++i) + if (ret[i] in deleted) { + s = 1 + break + } + if (s) + break + + for (i = 1; i <= n; ++i) + deleted[ret[i]] + + if (n == 1) + continue + + $0 = mydata[o,ret[n]] + start1 = $3 + if ($5 == "+") { + start2 = $8 + } else { + end2 = $9 + } + + $0 = mydata[o,ret[1]] + end1 = $4 + if ($5 == "+") { + end2 = $9 + } else { + start2 = $8 + } + s = $1 "\t" $2 "\t" start1 "\t" end1 "\t" $5 "\t" $6 "\t" $7 "\t" start2 "\t" end2 "\t0\t0\t0\tAS:i:" int(best[ret[1]]) "\t" "CH:i:" n "\tcg:Z:" + + + $0 = mydata[o,ret[n]] + end1 = $4 + if ($5 == "+") + end2 = $9 + else + end2 = $8 + + #construct combined cigar + c = cigar[ret[n]] + + for (i = n - 1; i >= 1; --i) { + j = ret[i] + $0 = mydata[o,j] + + gl1 = $3 - end1 + end1 = $4 + if ($5 == "+") { + gl2 = $8 - end2 + end2 = $9 + } + else { + gl2 = end2 - $9 + end2 = $8 + } + if (trim[j] > 0) { + gl1 += trim[j] + gl2 += trim[j] + } + + if (gl1 < 0 || gl2 < 0) { + print "\nerror:negative gap" gl1 gl2 + exit + } + if (gl1 > 0) + c = c gl1 "I" + if (gl2 > 0) + c = c gl2 "D" + if (trim[j] > 0) + c = c "" trim1(cigar[j], trim[j]) + else + c = c "" cigar[j] + + } + if (n > 1) + print s c + } #while (1) + + k = 1 + for (j = 1; j <= cother[o]; ++j) { + mydata[o, k] = mydata[o, j] + if (!(j in deleted)) + ++k; + } + cother[o] = k - 1 + delete(deleted) + } + } + } +} +#how much you can trim cigar aligment from the first (match) +function maxTrim1(cigar) +{ + return cigar+0 +} +#and remove overlap from first match +function trim1(cigar,ov){ + return (cigar - ov) substr(cigar, index(cigar, "M")) +} + +#how much you can trim cigar aligment from the last (match) +function maxTrim2(cigar ,i) +{ + i = length(cigar) - 1 + while (substr(cigar, i, 1) ~ /[0-9]/) + --i + + return substr(cigar, i + 1) + 0 +} +#function trim1(cigar, ov ,s1,s2,le,op){ +# s1 = ov +# s2 = ov +# while (1) { +# le = (cigar + 0) +# op = substr(cigar, length(le) + 1, 1) +# if (op == "M") { +# if (s1 - le <= 0 && s2 - le <= 0) { +# if (s1 <= 0 && s2 <= 0) +# return cigar +# return (cigar - max(s1, s2)) substr(cigar, length(le) + 1) +# } +# s1 -= le +# s2 -= le +# } else if (op == "I") { +# s1 -= le +# } +# else if (op == "D") { +# s2 -= le +# } +# else { +# print "error parsing cigar string" +# exit +# } +# cigar = substr(cigar, length(le) + 2) +# } +#} + diff --git a/software/LepAnchor/scripts/cutBed.awk b/software/LepAnchor/scripts/cutBed.awk new file mode 100644 index 0000000..0ab51a0 --- /dev/null +++ b/software/LepAnchor/scripts/cutBed.awk @@ -0,0 +1,152 @@ +#cuts bed based on extra cut sites +#usage: awk -f cutBed.awk map.bed cut_sites.txt >cut.bed +# awk -f cutBed_fixN.awk contigs.length cut.bed >cut_fix.bed +#map.bed is from Map2Bed +#cut_sites is contig, start [,stop] +#both input files with non-overlapping intervals and sorted by start +BEGIN{ +# FS="\t" + OFS="\t" +} +#store bed +(NR==FNR && /^[^#]/){ + end[$1] = substr($3, index($3, "-") + 1) + 0 + if (!($1 in c)) + start[$1] = $2 + 0 + ++c[$1] + bed[$1,c[$1]]=$0 + beds[$1,c[$1]] = substr($2, index($2, "-") + 1) + 0 + bede[$1,c[$1]] = $3 + 0 + bedc[$1,c[$1]] = $5 + contigs[$1] + if ($5 > maxChr) + maxChr = $5 +} +#store cutsites +(NR!=FNR){ + if (NF < 2) + next + #gap can be in ($3-$2+2) positions, start-1...end are the possible positions + if (NF >= 3) + $2 = $2 - 1 + else + $3 = $2 + + #remove cut if it intersects several contig boundaries + #trim if only one... + trim = 0 + for (i = 1; i <= c[$1]; ++i) { + if ($2 < beds[$1,i] && $3 >= beds[$1,i]) { + if (trim > 0) { + print "cutBed: " $0 " removed" >"/dev/stderr" + next + } + trim = i + } + if ($2 <= bede[$1,i] && $3 > bede[$1,i]) { + if (trim > 0) { + print "cutBed: " $0 " removed" >"/dev/stderr" + next + } + trim = i + } + } + if (trim > 0) + for (i = 1; i <= c[$1]; ++i) { + if ($2 < beds[$1,i] && $3 >= beds[$1,i]) { + printf "cutBed: " $0 " trimmed => " >"/dev/stderr" + $3 = beds[$1,i] - 1 + print $0 >"/dev/stderr" + + } + if ($2 <= bede[$1,i] && $3 > bede[$1,i]) { + printf "cutBed: " $0 " trimmed => " >"/dev/stderr" + $2 = bede[$1,i] + 1 + print $0 >"/dev/stderr" + } + } + cut1[$1,++d[$1]] = $2 + cut2[$1, d[$1]] = $3 +} +END{ + for (contig in contigs) { + if (d[contig] + 0 == 0) { #no cuts + for (i = 1; i <= c[contig]; ++i) + print bed[contig, i] + } else { #at least one cut for contig + j = 1 + i = 1 + st = start[contig] + + #before bed i, split zero chr + printed = 0 + while (j <= d[contig] && cut2[contig, j] < beds[contig, i]) { + c1 = cut1[contig, j] + c2 = cut2[contig, j] + for (chr = 1; chr <= maxChr; ++chr) ## put to all chromosomes + print contig, st, c1"-"c2, "?", chr + st = (c1+1)"-"(c2+1) + ++j + printed = 1 + } + if (printed == 0) + st = st "-" beds[contig, i] + + while (i <= c[contig]) { + #split bed i, chr in bedc + #printed = 0 + while (j <= d[contig] && cut1[contig, j] >= beds[contig, i] && cut2[contig, j] <= bede[contig, i]) { + c1 = cut1[contig, j] + c2 = cut2[contig, j] + print contig, st, c1"-"c2, "?", bedc[contig, i] + st = (c1+1)"-"(c2+1) + ++j + #printed = 1 + } + + #after bed i, chr in bedc for first + printed = 0 + while (j <= d[contig] && cut1[contig, j] > bede[contig, i] && (i == c[contig] || cut2[contig, j] < beds[contig, i + 1])) { + c1 = cut1[contig, j] + c2 = cut2[contig, j] + if (printed == 0) { + print contig, st, c1"-"c2, "?", bedc[contig, i] + printed = 1 + } else + for (chr = 1; chr <= maxChr; ++chr) ## put to all chromosomes + print contig, st, c1"-"c2, "?", chr + st = (c1+1)"-"(c2+1) + ++j + } + if (printed == 0) { + if (i == c[contig]) + e2 = end[contig]"*" + else + e2 = beds[contig, i + 1] - 1 + e1 = bede[contig, i] + print contig, st, e1"-"e2, "?", bedc[contig, i] + st = (e1+1)"-"(e2+1) + } else if (i == c[contig]) { + for (chr = 1; chr <= maxChr; ++chr) ## put to all chromosomes + print contig, st, end[contig]"*", "?", chr + + } + + ++i + } + } + } + for (contig in d) + if (!(contig in contigs)){ + st = 1 + for (j = 1; j <= d[contig]; ++j) { + c1 = cut1[contig, j] + c2 = cut2[contig, j] + for (chr = 1; chr <= maxChr; ++chr) ## put to all chromosomes + print contig, st, c1"-"c2,"?",chr + st = (c1+1)"-"(c2+1) + } + for (chr = 1; chr <= maxChr; ++chr) ## put to all chromosomes + print contig, st, "N*","?",chr + } +} diff --git a/software/LepAnchor/scripts/cutBed_fixN.awk b/software/LepAnchor/scripts/cutBed_fixN.awk new file mode 100644 index 0000000..a484163 --- /dev/null +++ b/software/LepAnchor/scripts/cutBed_fixN.awk @@ -0,0 +1,23 @@ +#awk -f cutBed_fixN.awk contigs.length cut.bed +BEGIN{ +# FS="\t" + OFS="\t" +} +(NR==FNR){ + l[$1]=$2 +} +(NR!=FNR){ + if ($3 == "N*") { + if ($1 in l) + $3 = l[$1]"*" + else { + print "error: length for contig " $1 " not found!" + exit + } + } + d[++line] = $0 +} +END{ + for (i = 1; i <= line; ++i) + print d[i] +} diff --git a/software/LepAnchor/scripts/hic.awk b/software/LepAnchor/scripts/hic.awk new file mode 100644 index 0000000..48bbf0a --- /dev/null +++ b/software/LepAnchor/scripts/hic.awk @@ -0,0 +1,33 @@ +#samtools view hic.bam|awk -f hic.awk +#or samtools view -h hic.bam|samblaster/samblaster -r|awk -f hic.awk +#hic.bam: bwa mem -t16 -5SPM REF R1.fq.gz R2.fq.gz|samtools view -b - >hic.bam +#process hic data for Lep-Anchor +BEGIN{ + #mapping quality limit + if (qLimit == "") + qLimit=20 + FS="\t" +} +(NF>=7 && $5>=qLimit){ + if ($1 == prevR) { + for (i = 1; i <= n; ++i) { #print both ways + print $3"\t"$4"\t"c[i]"\t"p[i]"\t?\t"min(q[i], $5) + print c[i]"\t"p[i]"\t"$3"\t"$4"\t?\t"min(q[i], $5) + } + ++n + } else + n = 1 + c[n]=$3 + p[n]=$4 + q[n]=$5 + prevR = $1 + +} +function min(a,b){ + if (a <= b) + return a + else + return b +} +END{ +} diff --git a/software/LepAnchor/scripts/ld.awk b/software/LepAnchor/scripts/ld.awk new file mode 100644 index 0000000..113eabc --- /dev/null +++ b/software/LepAnchor/scripts/ld.awk @@ -0,0 +1,22 @@ +#zcat sorted_ld.gz|awk -f perm|awk -f maxmatch3 +#awk -vOFS="\t" '(!(($1"\t"$2"\t"$3) in d) && !(($3"\t"$4"\t"$1) in d) ){d[$1"\t"$2"\t"$3];d[$3"\t"$4"\t"$1]; print;t=$1;$1=$3;$3=t;t=$2;$2=$4;$4=t;print}' +BEGIN{ + if (window == "") + window = 10000 +} +function round(p) +{ + return 1 + window * int((p-1)/window) +} +{ + r1 = round($2) + r2 = round($4) + i1 = $1"\t"$2"\t"$3"\t"r2"\t"r1 + i2 = $3"\t"$4"\t"$1"\t"r1"\t"r2 + if (d[i1] == "" && d[i2] == "") { + ++d[i1] + ++d[i2] + print $1"\t"r1"\t"$3"\t"r2"\t"$5"\t"$6"\t"$2"\t"$4 + print $3"\t"r2"\t"$1"\t"r1"\t"$5"\t"$6"\t"$4"\t"$2 + } +} diff --git a/software/LepAnchor/scripts/lepanchor_wrapper.sh b/software/LepAnchor/scripts/lepanchor_wrapper.sh new file mode 100755 index 0000000..67c4b37 --- /dev/null +++ b/software/LepAnchor/scripts/lepanchor_wrapper.sh @@ -0,0 +1,268 @@ +#!/bin/bash +########################################################## +# +# Lep-Anchor wrapper +# +# usage: lepanchor_wrapper.sh -f ref.fasta -n num_chr -e extra_cut_site_file -c chain_file -p paf_file -r prox_file -m map_file1 -m map_file2, ... +# +# output: LA_REF.fa.gz anchored reference genome +# LA_REF.agp agp file describing the genome +# marey*.png Marey maps for visual verification +# chr*.agp agp files for each chromosome +# scaffolds_chr*.agp agp files for each chromosome in scaffolds (each block of linked contigs as a scaffold) +# +# +# Pasi Rastas, (c) 2020, pasi.rastas@gmail.com +# +########################################################## + +#if [ "$#" -ne 3 ]; then +# echo "At least three input parameters must be provided" +# exit 1 +#fi + + +#parse parameters + +function print_usage() +{ +echo "##########################################################" +echo "#" +echo "# Lep-Anchor wrapper" +echo "#" +echo "# usage: lepanchor_wrapper.sh -t threads -f ref.fasta -n num_chr -e extra_cut_site_file -c chain_file -r prox_file -p paf_file -m map_file1 -m map_file2, ... " +echo "#" +echo "# download Lep-Anchor by" +echo "# wget https://sourceforge.net/projects/lep-anchor/files/binary%2Bcode.zip/download -O la.zip;unzip la.zip" +echo "#" +echo "# Pasi Rastas, (c) 2020, pasi.rastas@gmail.com" +echo "#" +echo "##########################################################" +} + +while getopts ":e:n:c:p:m:f:t:r:" OPTION; do + case ${OPTION} in + t) + THREADS=$OPTARG;; + n) + CHR=$OPTARG;; + c) + CHAIN=$OPTARG;; + p) + PAF=$OPTARG;; + e) + CUT=$OPTARG;; + r) + PROX=$OPTARG;; + m) + MAP="$MAP $OPTARG";; + f) + REF=$OPTARG;; + *) + echo "Incorrect options provided" + exit 1;; + esac +done + +if [[ $MAP =~ ^$ ]];then + print_usage + echo "Please provide at least one map" + exit 1 +fi + +if [[ $REF =~ ^$ ]];then + if [[ ! -e "contigs.length" ]];then + print_usage + echo "Please provide either reference fasta or contigs.length file" + exit 1 + fi +fi + +if [[ $CHR =~ ^[0-9]+$ ]];then + echo "Number of chromosomes = $CHR" +else + print_usage + echo "Please provide parameter n (number of chromosomes)" + exit 1 +fi + +if [[ $CHAIN =~ ^$ ]];then + echo "No chain provided" + CHAIN="/dev/null" +fi + +if [[ $PROX =~ ^$ ]];then + echo "No proximity data provided" + PROX="/dev/null" +fi + + +if [[ $PAF =~ ^$ ]];then + echo "No paf provided" + PAF="/dev/null" +fi + +#number of threads +if [[ $THREADS =~ ^$ ]];then + THREADS=8 +fi + +#get Lep-Anchor +#wget https://sourceforge.net/projects/lep-anchor/files/binary%2Bcode.zip/download -O la.zip +#unzip la.zip + +#where Lep-Anchor binaries are located +LABIN=bin/ + +#java runtime located here +JAVA=java + +#parallel +if ! command -v "parallel" +then + echo "command parallel not found, using only one thread" + PARALLEL=bash +else + PARALLEL="parallel --jobs $THREADS" +fi + +if [[ ! $REF =~ ^$ ]];then + echo "calculating contigs.length file..." + echo "gunzip -fc $REF|awk -f contigLength.awk >contigs.length"|bash +fi + + +echo "finding full haplotypes..." +echo "gunzip -fc $CHAIN|awk -f findFullHaplotypes.awk >fullHaplotypes50.txt"|bash +wc -l fullHaplotypes50.txt + + +echo "running liftoverHaplotypes for all input maps..." +for i in $MAP +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN LiftoverHaplotypes map=$i haplotypes=fullHaplotypes50.txt chain=- >$i.liftover" +done|$PARALLEL + +#store lift overed maps to $MAPL +for i in $MAP +do + MAPL="$MAPL $i.liftover" +done + + +#make input for CleanMap +for i in $MAP +do +cat $i.liftover +done|sort -V -k 1,1 -k 2,2n >map_all_sorted.liftover + + +#CleanMap +echo "running CleanMap..." +$JAVA -cp $LABIN CleanMap map=map_all_sorted.liftover >map_all.clean + +#Map2Bed +echo "running Map2Bed..." +$JAVA -cp $LABIN Map2Bed map=map_all.clean contigLength=contigs.length >map.bed + +if [[ ! $CUT =~ ^$ ]];then +echo "adding extra cuts to map.bed..." +awk '{print $1"\t"$2"\t"$3}' $CUT|sort -V -k 1,1 -k 2,2n|awk -f cutBed.awk map.bed - >map.bed.tmp +awk -f cutBed_fixN.awk contigs.length map.bed.tmp >map.bed +fi + + +#find contigs not put into chromosomes +cut -f 1 contigs.length|grep -v -w -F -f <(cut -f 2 fullHaplotypes50.txt; cut -f 1 map.bed) >not_used.txt + +grep -w -F -f not_used.txt contigs.length|awk -vn=$CHR '{s=$1"\t1\t"$2"\t?\t"; for (i=1;i<=n;++i) print s i}' >chr0.bed +cat map.bed chr0.bed >map_extra.bed + + +#PlaceAndOrientContigs +echo "running PlaceAndOrientContigs (first iteration)..." +for i in $(seq $CHR) +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN PlaceAndOrientContigs bed=map_extra.bed chromosome=$i map=$MAPL chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=1 >chr$i.la 2>chr$i.la.err" +done|$PARALLEL + + +#propagate +echo "running propagate..." + +awk -f propagate.awk chr*.la >tmp1.la +awk -f propagate.awk tmp1.la >tmp2.la +i=2 + +while ! cmp -s "tmp$i.la" "tmp$(( $i-1 )).la" ;do + awk -f propagate.awk tmp$i.la >tmp$[$i+1].la + i=$[$i+1] +done + +#create prop*.la +awk '/^[^#]/{++d[$1 "\t" $7+0 "\t" $8+0]; data[++line]=$0}END{for (i = 1; i <= line; ++i) {$0=data[i];if (d[$1 "\t" $7+0 "\t" $8+0] == 1) fn="prop"$5".la"; else if ($5==1) fn="prop0.la"; else fn=""; if (fn != "") print $0>fn}}' tmp$i.la + +#use propagate2 to include possible bridge contigs as well... +#awk -f propagate2.awk tmp$i.la|awk '(/^[^#]/ && NF>=8){++d[$1 "\t" $7+0 "\t" $8+0]; data[++line]=$0}END{for (i = 1; i <= line; ++i) {$0=data[i];if (d[$1 "\t" $7+0 "\t" $8+0] == 1) fn="prop"$5".la"; else if ($5==1) fn="prop0.la"; else fn=""; if (fn != "") print $0>fn}}' + +#create a new bed by combining prop[1-9]*.la and map.bed +awk '(NR==FNR){print;c[$1]}(NR!=FNR && !($1 in c)){print $1 "\t" $7+0 "\t" $8+0"\t?\t"$5}' map.bed prop[1-9]*.la >map_prop.bed + +#PlaceAndOrientContigs +echo "running PlaceAndOrientContigs (second iteration)..." +for i in $(seq $CHR) +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN PlaceAndOrientContigs bed=map_prop.bed chromosome=$i map=$MAPL chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=1 >ichr$i.la 2>ichr$i.la.err" +done|$PARALLEL + +#pruning contig blocks without map support +for i in $(seq $CHR) +do + awk -f prune.awk ichr$i.la >ichr${i}_pruned.la +done 2>pruned.la + +#remove overlap(s) +awk -f removeOverlaps.awk map_prop.bed ichr*_pruned.la >iall.la + +#construct agp files +for i in $(seq $CHR) +do +awk -vn=$i '($5==n)' iall.la|awk -vprefix="LG" -vlg=$i -f makeagp_full2.awk - >chr$i.agp +awk -vn=$i '($5==n)' iall.la|awk -vprefix="LG" -vlg=$i -f makeagp2.awk - >scaffolds_chr$i.agp +done + +#find contigs not used +cut -f 1 contigs.length|grep -v -w -F -f <(cut -f 2 fullHaplotypes50.txt;awk '($5!="U"){print $6}' chr*.agp) >not_used_final.txt + +grep -F -w -f not_used_final.txt contigs.length|awk '{print $1,1,$2,1,"W",$1,1,$2,"+"}' >not_used.agp + +cat chr*.agp not_used.agp >REF_LA.agp +#one could use scaffolds_chr*.agp as well instead of chr*.agp +cat scaffolds_chr*.agp not_used.agp >REF_LA_scaffolds.agp + +#make final fasta +if [[ ! $REF =~ ^$ ]];then + echo "constructing final fasta (REF_LA.fa.gz)..." + echo "gunzip -fc $REF|awk -f makefasta.awk - REF_LA.agp|gzip >REF_LA.fa.gz"|bash + + echo "constructing final fasta (REF_LA_scaffolds.fa.gz)..." + echo "gunzip -fc $REF|awk -f makefasta.awk - REF_LA_scaffolds.agp|gzip >REF_LA_scaffolds.fa.gz"|bash +fi + +#construct Marey map +echo "constructing Marey maps... (marey*.png)" +j=1 +for m in $MAP +do + for c in $(seq $CHR) + do +# awk -vn=$c '($3==n)' $m.liftover|awk -f liftover.awk chr$c.agp -|awk -vm=$j '(/LG/ && NF>=4){if (NF==4) $5=$4;print $1"\t"$2"\t"$3"\t"m"\t"$4"\t"$5}' + awk -vn=$c '($3==n)' $m.liftover|awk -f liftover.awk chr$c.agp -|awk -vm=$j '(/LG/ && NF>=4){if (NF==4) {print $1"\t"$2"\t"$3"\t"m"\t"$4"\t"$4} else for (i=4;imarey.data.gz + +Rscript plot_marey.R + +#TODO: lepanchor_wrapper_step2.sh + diff --git a/software/LepAnchor/scripts/lepanchor_wrapper2.sh b/software/LepAnchor/scripts/lepanchor_wrapper2.sh new file mode 100755 index 0000000..a5f7991 --- /dev/null +++ b/software/LepAnchor/scripts/lepanchor_wrapper2.sh @@ -0,0 +1,287 @@ +#!/bin/bash +########################################################## +# +# Lep-Anchor wrapper, version 2 +# +# usage: lepanchor_wrapper2.sh -f ref.fasta -e extra_cut_site_file -n num_chr -c chain_file -p paf_file -r prox_file -m map_file1 -m map_file2, ... +# +# output: REF_LA.fa.gz anchored reference genome +# REF_LA.agp agp file describing the genome +# REF_LA_scaffolds.fa.gz anchored reference genome in scaffolds +# REF_LA_scaffolds.agp anchored reference genome in scaffolds agp file +# marey*.png Marey maps for visual verification +# +# chr*.agp agp files for each chromosome +# scaffolds_chr*.agp agp files for each chromosome in scaffolds (each block of linked contigs as a scaffold) +# +# +# Pasi Rastas, (c) 2021, pasi.rastas@gmail.com +# +########################################################## + +#if [ "$#" -ne 3 ]; then +# echo "At least three input parameters must be provided" +# exit 1 +#fi + + +#parse parameters + +function print_usage() +{ +echo "##########################################################" +echo "#" +echo "# Lep-Anchor wrapper2" +echo "#" +echo "# usage: lepanchor_wrapper2.sh -T thread_per_run -t threads -f ref.fasta -n num_chr -e extra_cut_site_file -c chain_file -r prox_file -p paf_file -m map_file1 -m map_file2, ... " +echo "#" +echo "# download Lep-Anchor by" +echo "# wget https://sourceforge.net/projects/lep-anchor/files/binary%2Bcode.zip/download -O la.zip;unzip la.zip" +echo "#" +echo "# Pasi Rastas, (c) 2021, pasi.rastas@gmail.com" +echo "#" +echo "##########################################################" +} + +while getopts ":e:n:c:p:m:f:t:r:T:" OPTION; do + case ${OPTION} in + T) + THREADS2=$OPTARG;; + t) + THREADS=$OPTARG;; + n) + CHR=$OPTARG;; + c) + CHAIN=$OPTARG;; + p) + PAF=$OPTARG;; + e) + CUT=$OPTARG;; + r) + PROX=$OPTARG;; + m) + MAP="$MAP $OPTARG";; + f) + REF=$OPTARG;; + *) + echo "Incorrect options provided" + exit 1;; + esac +done + +if [[ $MAP =~ ^$ ]];then + print_usage + echo "Please provide at least one map" + exit 1 +fi + +if [[ $REF =~ ^$ ]];then + if [[ ! -e "contigs.length" ]];then + print_usage + echo "Please provide either reference fasta or contigs.length file" + exit 1 + fi +fi + +if [[ $CHR =~ ^[0-9]+$ ]];then + echo "Number of chromosomes = $CHR" +else + print_usage + echo "Please provide parameter n (number of chromosomes)" + exit 1 +fi + +if [[ $CHAIN =~ ^$ ]];then + echo "No chain provided" + CHAIN="/dev/null" +fi + +if [[ $PROX =~ ^$ ]];then + echo "No proximity data provided" + PROX="/dev/null" +fi + + +if [[ $PAF =~ ^$ ]];then + echo "No paf provided" + PAF="/dev/null" +fi + +#number of threads +if [[ $THREADS =~ ^$ ]];then + THREADS=8 +fi + +if [[ $THREADS2 =~ ^$ ]];then + THREADS2=1 +fi + +#get Lep-Anchor +#wget https://sourceforge.net/projects/lep-anchor/files/binary%2Bcode.zip/download -O la.zip +#unzip la.zip + +#where Lep-Anchor binaries are located +LABIN=bin/ + +#java runtime located here +JAVA=java + +#parallel +if ! command -v "parallel" +then + echo "command parallel not found, using only one thread" + PARALLEL=bash +else + PARALLEL="parallel --jobs $THREADS" +fi + +if [[ ! $REF =~ ^$ ]];then + echo "calculating contigs.length file..." + echo "gunzip -fc $REF|awk -f contigLength.awk >contigs.length"|bash +fi + + +echo "finding full haplotypes..." +echo "gunzip -fc $CHAIN|awk -f findFullHaplotypes.awk >fullHaplotypes50.txt"|bash +wc -l fullHaplotypes50.txt + + +echo "running liftoverHaplotypes for all input maps..." +for i in $MAP +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN LiftoverHaplotypes map=$i haplotypes=fullHaplotypes50.txt chain=- >$i.liftover" +done|$PARALLEL + +#store lift overed maps to $MAPL +for i in $MAP +do + MAPL="$MAPL $i.liftover" +done + +#make input for CleanMap +for i in $MAPL +do +cat $i +done|sort -V -k 1,1 -k 2,2n >map_all_sorted.liftover + + +#CleanMap +echo "running CleanMap..." +$JAVA -cp $LABIN CleanMap map=map_all_sorted.liftover >map_all.clean + +#Map2Bed +echo "running Map2Bed..." +$JAVA -cp $LABIN Map2Bed map=map_all.clean contigLength=contigs.length >map.bed + +if [[ ! $CUT =~ ^$ ]];then +echo "adding extra cuts to map.bed..." +awk '{print $1"\t"$2"\t"$3}' $CUT|sort -V -k 1,1 -k 2,2n|awk -f cutBed.awk map.bed - >map.bed.tmp +awk -f cutBed_fixN.awk contigs.length map.bed.tmp >map.bed +fi + + +#find contigs not put into chromosomes +cut -f 1 contigs.length|grep -v -w -F -f <(cut -f 2 fullHaplotypes50.txt; cut -f 1 map.bed) >not_used.txt +#cut -f 1 contigs.length|grep -v -w -F -f <(cut -f 1 map.bed) >not_used.txt + + +grep -w -F -f not_used.txt contigs.length|awk -vn=$CHR '{s=$1"\t1\t"$2"\t?\t"; for (i=1;i<=n;++i) print s i}' >chr0.bed +cat map.bed chr0.bed >map_extra.bed + +#PlaceAndOrientContigs +echo "running PlaceAndOrientContigs (first iteration)..." +for i in $(seq $CHR) +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN PlaceAndOrientContigs numThreads=$THREADS2 bed=map_extra.bed chromosome=$i map=$MAPL chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=0 >chr$i.la 2>chr$i.la.err" +done|$PARALLEL + +#propagate4 +echo "running propagate4..." +awk -f propagate4.awk pass=1 chr*[0-9].la pass=2 chr*[0-9].la.err|awk -f pickbed.awk - map_extra.bed >map_extra_prop.bed + +#PlaceAndOrientContigs +echo "running PlaceAndOrientContigs (second iteration)..." +for i in $(seq $CHR) +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN PlaceAndOrientContigs numThreads=$THREADS2 $(awk -f pickorientation.awk chr$i.la) bed=map_extra_prop.bed chromosome=$i map=$MAPL chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=1 >ichr$i.la 2>ichr$i.la.err" +done|$PARALLEL + +#propagate +echo "running propagate..." + +awk -f propagate.awk ichr*.la >tmp1.la +awk -f propagate.awk tmp1.la >tmp2.la +i=2 + +while ! cmp -s "tmp$i.la" "tmp$(( $i-1 )).la" ;do + awk -f propagate.awk tmp$i.la >tmp$[$i+1].la + i=$[$i+1] +done + +#create prop*.la, take only contig put to uniquely to a chromosome +#use propagate2 to include possible bridge contigs as well... +awk -f propagate2.awk tmp$i.la|awk '(/^[^#]/ && NF>=8){++d[$1"\t"($7+0)"\t"($8+0)]; data[++line]=$0}END{for (i=1; i<=line; ++i) {$0=data[i];if (d[$1"\t"($7+0)"\t"($8+0)] == 1) {fn="prop"$5".la";print $0>fn}}}' + +#create a new bed by combining prop[1-9]*.la and map_extra_prop.bed +awk '{print $1"\t"($7+0)"\t"($8+0)"\t?\t"$5}' prop[1-9]*.la|awk -f pickbed.awk - map_extra_prop.bed >map_extra_prop2.bed + +#third iteration could be just improveOrder + +#PlaceAndOrientContigs +echo "running PlaceAndOrientContigs (third iteration)..." +for i in $(seq $CHR) +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN PlaceAndOrientContigs numThreads=$THREADS2 $(awk -f pickorientation.awk chr$i.la) bed=map_extra_prop2.bed chromosome=$i map=$MAPL chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=1 evaluateAnchoring=prop$i.la improveAnchoring=1 numRuns=1 >iichr$i.la 2>iichr$i.la.err" +done|$PARALLEL + +#pruning contig blocks without map support +for i in $(seq $CHR) +do + awk -f prune.awk iichr$i.la >iichr${i}_pruned.la +done 2>pruned.la + +#remove overlap(s) +awk -f removeOverlaps.awk map_extra_prop2.bed iichr*_pruned.la >iiall.la + +#construct agp files +for i in $(seq $CHR) +do +awk -vn=$i '($5==n)' iiall.la|awk -vprefix="LG" -vlg=$i -f makeagp_full2.awk - >chr$i.agp +awk -vn=$i '($5==n)' iiall.la|awk -vprefix="LG" -vlg=$i -f makeagp2.awk - >scaffolds_chr$i.agp +done + +#find contigs not used +cut -f 1 contigs.length|grep -v -w -F -f <(cut -f 2 fullHaplotypes50.txt;awk '($5!="U"){print $6}' chr*.agp) >not_used_final.txt + +grep -F -w -f not_used_final.txt contigs.length|awk '{print $1,1,$2,1,"W",$1,1,$2,"+"}' >not_used.agp + +cat chr*.agp not_used.agp >REF_LA.agp +#one could use scaffolds_chr*.agp as well instead of chr*.agp +cat scaffolds_chr*.agp not_used.agp >REF_LA_scaffolds.agp + +#make final fasta +if [[ ! $REF =~ ^$ ]];then + echo "constructing final fasta (REF_LA.fa.gz)..." + echo "gunzip -fc $REF|awk -f makefasta.awk - REF_LA.agp|gzip >REF_LA.fa.gz"|bash + + echo "constructing final fasta (REF_LA_scaffolds.fa.gz)..." + echo "gunzip -fc $REF|awk -f makefasta.awk - REF_LA_scaffolds.agp|gzip >REF_LA_scaffolds.fa.gz"|bash +fi + +#construct Marey map +echo "constructing Marey maps... (marey*.png)" +j=1 +for m in $MAP +do + for c in $(seq $CHR) + do +# awk -vn=$c '($3==n)' $m.liftover|awk -f liftover.awk chr$c.agp -|awk -vm=$j '(/LG/ && NF>=4){if (NF==4) $5=$4;print $1"\t"$2"\t"$3"\t"m"\t"$4"\t"$5}' + awk -vn=$c '($3==n)' $m.liftover|awk -f liftover.awk chr$c.agp -|awk -vm=$j '(/LG/ && NF>=4){if (NF==4) {print $1"\t"$2"\t"$3"\t"m"\t"$4"\t"$4} else for (i=4;imarey.data.gz + +Rscript plot_marey.R + +#TODO: lepanchor_wrapper_step2.sh + diff --git a/software/LepAnchor/scripts/lepanchor_wrapper3.sh b/software/LepAnchor/scripts/lepanchor_wrapper3.sh new file mode 100755 index 0000000..3df50a4 --- /dev/null +++ b/software/LepAnchor/scripts/lepanchor_wrapper3.sh @@ -0,0 +1,292 @@ +#!/bin/bash +########################################################## +# +# Lep-Anchor wrapper, version 3 (allopolyploid version) +# +# usage: lepanchor_wrapper3.sh -f ref.fasta -e extra_cut_site_file -n num_chr -c chain_file -p paf_file -r prox_file -m map_file1 -m map_file2, ... +# +# output: REF_LA.fa.gz anchored reference genome +# REF_LA.agp agp file describing the genome +# REF_LA_scaffolds.fa.gz anchored reference genome in scaffolds +# REF_LA_scaffolds.agp anchored reference genome in scaffolds agp file +# marey*.png Marey maps for visual verification +# +# chr*.agp agp files for each chromosome +# scaffolds_chr*.agp agp files for each chromosome in scaffolds (each block of linked contigs as a scaffold) +# +# +# Pasi Rastas, (c) 2021, pasi.rastas@gmail.com +# +########################################################## + +#if [ "$#" -ne 3 ]; then +# echo "At least three input parameters must be provided" +# exit 1 +#fi + + +#parse parameters + +function print_usage() +{ +echo "##########################################################" +echo "#" +echo "# Lep-Anchor wrapper3 (allopolyploid)" +echo "#" +echo "# usage: lepanchor_wrapper3.sh -T thread_per_run -t threads -f ref.fasta -e extra_cut_site_file -n num_chr -c chain_file -r prox_file -p paf_file -m map_file1 -m map_file2, ... " +echo "#" +echo "# download Lep-Anchor by" +echo "# wget https://sourceforge.net/projects/lep-anchor/files/binary%2Bcode.zip/download -O la.zip;unzip la.zip" +echo "#" +echo "# Pasi Rastas, (c) 2021, pasi.rastas@gmail.com" +echo "#" +echo "##########################################################" +} + +while getopts ":e:n:c:p:m:f:t:r:T:" OPTION; do + case ${OPTION} in + T) + THREADS2=$OPTARG;; + t) + THREADS=$OPTARG;; + n) + CHR=$OPTARG;; + c) + CHAIN=$OPTARG;; + p) + PAF=$OPTARG;; + e) + CUT=$OPTARG;; + r) + PROX=$OPTARG;; + m) + MAP="$MAP $OPTARG";; + f) + REF=$OPTARG;; + *) + echo "Incorrect options provided" + exit 1;; + esac +done + +if [[ $MAP =~ ^$ ]];then + print_usage + echo "Please provide at least one map" + exit 1 +fi + +if [[ $REF =~ ^$ ]];then + if [[ ! -e "contigs.length" ]];then + print_usage + echo "Please provide either reference fasta or contigs.length file" + exit 1 + fi +fi + +if [[ $CHR =~ ^[0-9]+$ ]];then + echo "Number of chromosomes = $CHR" +else + print_usage + echo "Please provide parameter n (number of chromosomes)" + exit 1 +fi + +if [[ $CHAIN =~ ^$ ]];then + echo "No chain provided" + CHAIN="/dev/null" +fi + +if [[ $PROX =~ ^$ ]];then + echo "No proximity data provided" + PROX="/dev/null" +fi + + +if [[ $PAF =~ ^$ ]];then + echo "No paf provided" + PAF="/dev/null" +fi + +#number of threads +if [[ $THREADS =~ ^$ ]];then + THREADS=8 +fi + +if [[ $THREADS2 =~ ^$ ]];then + THREADS2=1 +fi + +#get Lep-Anchor +#wget https://sourceforge.net/projects/lep-anchor/files/binary%2Bcode.zip/download -O la.zip +#unzip la.zip + +#where Lep-Anchor binaries are located +LABIN=bin/ + +#java runtime located here +JAVA=java + +#parallel +if ! command -v "parallel" +then + echo "command parallel not found, using only one thread" + PARALLEL=bash +else + PARALLEL="parallel --tmpdir . --jobs $THREADS" +fi + +if [[ ! $REF =~ ^$ ]];then + echo "calculating contigs.length file..." + echo "gunzip -fc $REF|awk -f contigLength.awk >contigs.length"|bash +fi + +#make input for CleanMap +for i in $MAP +do +cat $i +done|sort -V -k 1,1 -k 2,2n >map_all_sorted + + +#CleanMap +echo "running CleanMap..." +$JAVA -cp $LABIN CleanMap map=map_all_sorted >map_all.clean + +#Map2Bed +echo "running Map2Bed..." +$JAVA -cp $LABIN Map2Bed map=map_all.clean contigLength=contigs.length >map.bed + + +if [[ ! $CUT =~ ^$ ]];then +echo "adding extra cuts to map.bed..." +awk '{print $1"\t"$2"\t"$3}' $CUT|sort -V -k 1,1 -k 2,2n|awk -f cutBed.awk map.bed - >map.bed.tmp +awk -f cutBed_fixN.awk contigs.length map.bed.tmp >map.bed +fi + +#find contigs not put into chromosomes +cut -f 1 contigs.length|grep -v -w -F -f <(cut -f 1 map.bed) >not_used.txt + +grep -w -F -f not_used.txt contigs.length|awk -vn=$CHR '{s=$1"\t1\t"$2"\t?\t"; for (i=1;i<=n;++i) print s i}' >chr0.bed +cat map.bed chr0.bed >map_extra.bed + +#PlaceAndOrientContigs +echo "running PlaceAndOrientContigs (first iteration)..." +for i in $(seq $CHR) +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN PlaceAndOrientContigs numThreads=$THREADS2 bed=map_extra.bed chromosome=$i map=$MAP chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=0 >chr$i.la 2>chr$i.la.err" +done|$PARALLEL + +#propagate4 +echo "running propagate4..." +awk -f propagate4.awk pass=1 chr*[0-9].la pass=2 chr*[0-9].la.err|awk -f pickbed.awk - map_extra.bed >map_extra_prop.bed + +#PlaceAndOrientContigs +echo "running PlaceAndOrientContigs (second iteration)..." +for i in $(seq $CHR) +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN PlaceAndOrientContigs numThreads=$THREADS2 $(awk -f pickorientation.awk chr$i.la) bed=map_extra_prop.bed chromosome=$i map=$MAP chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=1 >ichr$i.la 2>ichr$i.la.err" +done|$PARALLEL + +#propagate +echo "running propagate..." + +awk -f propagate.awk ichr*.la >tmp1.la +awk -f propagate.awk tmp1.la >tmp2.la +i=2 + +while ! cmp -s "tmp$i.la" "tmp$(( $i-1 )).la" ;do + awk -f propagate.awk tmp$i.la >tmp$[$i+1].la + i=$[$i+1] +done + +#create prop*.la, take only contig put to uniquely to a chromosome +#use propagate2 to include possible bridge contigs as well... +awk -f propagate2.awk tmp$i.la|awk '(/^[^#]/ && NF>=8){++d[$1"\t"($7+0)"\t"($8+0)]; data[++line]=$0}END{for (i=1; i<=line; ++i) {$0=data[i];if (d[$1"\t"($7+0)"\t"($8+0)] == 1) {fn="prop"$5".la";print $0>fn}}}' + +#create a new bed by combining prop[1-9]*.la and map_extra_prop.bed +awk '{print $1"\t"($7+0)"\t"($8+0)"\t?\t"$5}' prop[1-9]*.la|awk -f pickbed.awk - map_extra_prop.bed >map_extra_prop2.bed + +#third iteration could be just improveOrder + +#PlaceAndOrientContigs +echo "running PlaceAndOrientContigs (third iteration)..." +for i in $(seq $CHR) +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN PlaceAndOrientContigs numThreads=$THREADS2 $(awk -f pickorientation.awk chr$i.la) bed=map_extra_prop2.bed chromosome=$i map=$MAP chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=1 evaluateAnchoring=prop$i.la improveAnchoring=1 numRuns=1 >iichr$i.la 2>iichr$i.la.err" +done|$PARALLEL + +echo "finding full haplotypes..." +awk '($NF=="haplotype")' iichr*.err|sort -n -r|awk -vlimit=2000 '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){h[$2,$3,$4]; print}' >fullHaplotypes50.txt +wc -l fullHaplotypes50.txt + +echo "running liftoverHaplotypes for all input maps..." +for i in $MAP +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN LiftoverHaplotypes map=$i haplotypes=fullHaplotypes50.txt chain=- >$i.liftover" +done|$PARALLEL + +#store lift overed maps to $MAPL +for i in $MAP +do + MAPL="$MAPL $i.liftover" +done + +awk -f removeHaplotypes.awk map_extra_prop2.bed fullHaplotypes50.txt > map_extra_prop2_nh.bed + +#PlaceAndOrientContigs +echo "running PlaceAndOrientContigs (fourth iteration)..." +for i in $(seq $CHR) +do +echo "gunzip -fc $CHAIN|$JAVA -cp $LABIN PlaceAndOrientContigs numThreads=$THREADS2 $(awk -f pickorientation.awk chr$i.la) bed=map_extra_prop2_nh.bed chromosome=$i map=$MAPL chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=1 evaluateAnchoring=iichr$i.la improveAnchoring=1 numRuns=1 >iiichr$i.la 2>iiichr$i.la.err" +done|$PARALLEL + + +#pruning contig blocks without map support +for i in $(seq $CHR) +do + awk -f prune.awk iiichr$i.la >iiichr${i}_pruned.la +done 2>pruned.la + +#remove overlap(s) +awk -f removeOverlaps.awk map_extra_prop2.bed iiichr*_pruned.la >iiall.la + +#construct agp files +for i in $(seq $CHR) +do +awk -vn=$i '($5==n)' iiall.la|awk -vprefix="LG" -vlg=$i -f makeagp_full2.awk - >chr$i.agp +awk -vn=$i '($5==n)' iiall.la|awk -vprefix="LG" -vlg=$i -f makeagp2.awk - >scaffolds_chr$i.agp +done + +#find contigs not used +cut -f 1 contigs.length|grep -v -w -F -f <(cut -f 2 fullHaplotypes50.txt;cut -f 1 iiall.la) >not_used_final.txt + +grep -F -w -f not_used_final.txt contigs.length|awk '{print $1,1,$2,1,"W",$1,1,$2,"+"}' >not_used.agp + +cat chr*.agp not_used.agp >REF_LA.agp +#one could use scaffolds_chr*.agp as well instead of chr*.agp +cat scaffolds_chr*.agp not_used.agp >REF_LA_scaffolds.agp + +#make final fasta +if [[ ! $REF =~ ^$ ]];then + echo "constructing final fasta (REF_LA.fa.gz)..." + echo "gunzip -fc $REF|awk -f makefasta.awk - REF_LA.agp|gzip >REF_LA.fa.gz"|bash + + echo "constructing final fasta (REF_LA_scaffolds.fa.gz)..." + echo "gunzip -fc $REF|awk -f makefasta.awk - REF_LA_scaffolds.agp|gzip >REF_LA_scaffolds.fa.gz"|bash +fi + +#construct Marey map +echo "constructing Marey maps... (marey*.png)" +j=1 +for m in $MAPL +do + for c in $(seq $CHR) + do + awk -vn=$c '($3==n)' $m|awk -f liftover.awk REF_LA.agp -|awk -vm=$j '(/LG/ && NF>=4){if (NF==4) {print $1"\t"$2"\t"$3"\t"m"\t"$4"\t"$4} else for (i=4;imarey.data.gz + +Rscript plot_marey.R + +#TODO: lepanchor_wrapper_step2.sh + diff --git a/software/LepAnchor/scripts/longread.awk b/software/LepAnchor/scripts/longread.awk new file mode 100644 index 0000000..8a97b97 --- /dev/null +++ b/software/LepAnchor/scripts/longread.awk @@ -0,0 +1,72 @@ +#awk -f longread.awk aln.paf +#aln.paf: minimap2 +#process long read alignment data for Lep-Anchor +BEGIN{ + #mapping quality limit + if (qLimit == "") + qLimit=1 + FS="\t" + rev["+"] = "-" + rev["-"] = "+" +} +(NF>=12 && $12>=qLimit){ + if ($1 == prevR) { + for (i = 1; i <= n; ++i) { + processOnePair($6,$8,$9,$3,$4,$5,c[i],start[i],end[i],rstart[i],rend[i],o[i],min(q[i], $12)) + } + ++n + } else + n = 1 + #print within contig part + print $6"\t"$8"\t"$6"\t"$9"\t++\t"$12 + print $6"\t"$9"\t"$6"\t"$8"\t--\t"$12 + o[n]=$5 #orientation + c[n]=$6 #contig + + start[n]=$8 #start + end[n]=$9 #end + + rstart[n]=$3 #read start + rend[n]=$4 #read end + + q[n]=$12 #quality + + prevR = $1 + +} +function min(a,b){ + if (a <= b) + return a + else + return b +} +function processOnePair(c1, start1, end1, rstart1, rend1, o1, c2, start2, end2, rstart2, rend2, o2, quality ,lp1,lp2,overlap) +{ + if (rstart1 > rstart2) { + processOnePair(c2, start2, end2, rstart2, rend2, o2, c1, start1, end1, rstart1, rend1, o1, quality) + return; + } + #now rstart1 <= rstart2 +# print "process" + overlap = 0 + if (rend1 > rstart2) + overlap = rend1 - rstart2 + overlap = int(overlap * 0.5) + + if (o1 == "+") + lp1 = end1 + 1 - overlap + else + lp1 = start1 + overlap + + if (o2 == "+") + lp2 = start2 + overlap + else + lp2 = end2 + 1 - overlap + + #print both ways + print c1"\t"lp1"\t"c2"\t"lp2"\t"o1 o2"\t"quality + print c2"\t"lp2"\t"c1"\t"lp1"\t"rev[o2] rev[o1]"\t"quality +# print "end" +} +END{ +} diff --git a/software/LepAnchor/scripts/makeagp2.awk b/software/LepAnchor/scripts/makeagp2.awk index 6b460f1..668df04 100644 --- a/software/LepAnchor/scripts/makeagp2.awk +++ b/software/LepAnchor/scripts/makeagp2.awk @@ -26,12 +26,16 @@ BEGIN{ pos = 0 n = "" } else if (gapLength > 0 && ($9=="null" || ($10!="null" && substr($10, 1, 5) != "chain"))) { #do not add gap for partial haplotypes - print prefix lg "." suffix "\t" pos + 1 "\t" pos + gapLength "\t" ++n "\tU\t" gapLength "\tcontig\tno\tna" - pos += gapLength + if ($9 != $1 || prevO != $4 || ((($4 ~ /+/) || $4=="?") && $2 != prevE + 1) || (($4 ~ /-/) && $3 != prevS - 1)) { #do not add gap if we have cut the same contig... + print prefix lg "." suffix "\t" pos + 1 "\t" pos + gapLength "\t" ++n "\tU\t" gapLength "\tcontig\tno\tna" + pos += gapLength + } } } print prefix lg "." suffix "\t" pos + 1 "\t" pos + ($3-$2+1) "\t" ++n "\tW\t" $1 "\t" $2 "\t" $3 "\t" map[$4] pos += ($3-$2+1) prevO = $4 + prevS = $2 + prevE = $3 } diff --git a/software/LepAnchor/scripts/makeagp_full2.awk b/software/LepAnchor/scripts/makeagp_full2.awk index 25e39de..3a8ddea 100644 --- a/software/LepAnchor/scripts/makeagp_full2.awk +++ b/software/LepAnchor/scripts/makeagp_full2.awk @@ -19,12 +19,16 @@ BEGIN{ (/^[^#]/ && /^[^$]/ && $3-$2>=0){ if (n != "") { if (gapLength > 0 && ($9=="null" || ($10!="null" && substr($10, 1, 5) != "chain"))) { #do not add gap for partial haplotypes - print prefix lg "\t" pos + 1 "\t" pos + gapLength "\t" ++n "\tU\t" gapLength "\tcontig\tno\tna" - pos += gapLength + if ($9 != $1 || prevO != $4 || ((($4 ~ /+/) || $4=="?") && $2 != prevE + 1) || (($4 ~ /-/) && $3 != prevS - 1)) { #do not add gap if we have cut the same contig... + print prefix lg "\t" pos + 1 "\t" pos + gapLength "\t" ++n "\tU\t" gapLength "\tcontig\tno\tna" + pos += gapLength + } } } print prefix lg "\t" pos + 1 "\t" pos + ($3-$2+1) "\t" ++n "\tW\t" $1 "\t" $2 "\t" $3 "\t" map[$4] pos += ($3-$2+1) prevO = $4 + prevS = $2 + prevE = $3 } diff --git a/software/LepAnchor/scripts/normalise.awk b/software/LepAnchor/scripts/normalise.awk new file mode 100644 index 0000000..3192958 --- /dev/null +++ b/software/LepAnchor/scripts/normalise.awk @@ -0,0 +1,62 @@ +#normalise proximity data based on within bin proximity +#awk -f normalise prox.data prox.data +BEGIN{ + OFS="\t" +} +(NR==FNR && $1==$3 && $2==$4){ #same position + sum2[$1,$2]+=$5 +} +(NR!=FNR) { + if (!first) { + for (i in sum2) { + s = sum2[i] + if (max == "" || max <= s) + max = s + if (min == "" || min >= s) + min = s + sum += s + sumS += s**2 + ++n + } + + avg = sum / n + iavg = 100.0 / avg + sd = sqrt((sumS + n * avg**2 - 2 * avg * sum)/(n - 1)) + print avg "\tsd=\t" sd "\tmin=\t" min "\tmax=\t" max "\tn=" n > "/dev/stderr" + first = 1 + } + s1 = sum2[$1,$2] + 0 + s2 = sum2[$3,$4] + 0 + $5 *= normalise(s1, s2) + print +} +function maxf(s1, s2) +{ + if (s1 >= s2) + return s1 + return s2 +} +function minf(s1, s2) +{ + if (s1 < s2) + return s1 + return s2 + +} +#maxf +function normalise(s1, s2) +{ + if (s1 <= 0.5 * avg && s2 <= 0.5 * avg) + return 2 * iavg + else + return 100.0 / maxf(s1, s2) +} +#minf +function normalise2(s1, s2) +{ + if (s1 <= avg || s2 <= avg) + return iavg + else + return 100.0 / minf(s1, s2) +} + diff --git a/software/LepAnchor/scripts/paf2chain.awk b/software/LepAnchor/scripts/paf2chain.awk new file mode 100644 index 0000000..66f6ee6 --- /dev/null +++ b/software/LepAnchor/scripts/paf2chain.awk @@ -0,0 +1,52 @@ +#makes a chain file from minimap2 (-c) paf +#minimap2 --no-long-join -c -PD -g 20000 -r 1000,40000 -k15 -w8 -A3 -B6 -O12 -E1 -s200 -z600 -N50 --min-occ-floor=100 -t 20 ref.fa ref.fa >ref-self.paf +#awk -f chainpaf.awk ref-self.paf|awk -f sortpaf.awk| sort -T . -n -r -k 1,1 | cut -f 2- > ref-self_sorted.paf +#awk -f paf2chain.awk rf-self_sorted.paf | gzip > paf.chain.gz +BEGIN{ + OFS="\t" + if (scale == "") + scale = 33 +} +($1!=$6){ + score = -1 + cigar = "" + for (i=13; i<=NF; ++i) { + if ($i ~ /^AS:i:/) + score = substr($i, 6)+0 + if ($i ~ /^cg:Z:/) + cigar = substr($i, 6) + } + if ($5 == "+") + print "chain\t" scale * score, $1, $2, "+", $3, $4, $6, $7, "+" ,$8, $9, NR + else + print "chain\t" scale * score, $1, $2, "+", $3, $4, $6, $7, "-" ,$7-$9, $7-$8, NR + + c = cigar + + d = 0 + i = 0 + prev = "" + do { + n = c+0 + o = substr(c, length(n)+1, 1) + c = substr(c, length(n)+2) +# print n " " o + if (o == "M") { + if (prev != "") + print prev, i, d + prev = n + i = 0 + d = 0 + } else if (o == "I") { + i+=n + } else if (o == "D") { + d+=n + } else { + print "error: cannot parse cigar operation " n " " o + exit + } + } while (c != "") + print prev +# print cigar +#"\t" cigar +} diff --git a/software/LepAnchor/scripts/plot_marey.R b/software/LepAnchor/scripts/plot_marey.R index 8bf6538..3cd3ced 100644 --- a/software/LepAnchor/scripts/plot_marey.R +++ b/software/LepAnchor/scripts/plot_marey.R @@ -1,57 +1,55 @@ -#! /usr/bin/env Rscript -# Written by Pasi Rastas (2021) -# modified by Pavel Dimens for Lep-Wrap (2021) - library(scales) -args <- commandArgs(trailingOnly = TRUE) - -# args[1] = path/to/marey.data.gz -# args[2] = directory/of/chrom AGP files - -map <- read.table(gzfile(args[1])) - -basedir <- dirname(args[1]) - -NN <- unique(map$V3) -MM <- unique(map$V4) - -cl <- rainbow(length(MM)) - -for (lg in NN) { - #print(lg) - png(paste0(basedir, "/LG.", lg ,".mareymap", ".png"), width = 1024, height = 1024) - ymax <- 0 - for (m in MM) { - map_tmp <- map[map$V3 == lg & map$V4 == m, ] - ymax <- max(ymax, max(map_tmp$V5)) - } - - index <- 1 - for (m in MM) { - map_tmp <- map[map$V3 == lg & map$V4 == m, ] - if (glm(map_tmp$V2 ~ map_tmp$V5)$coefficients[2] < 0) { - map_tmp$V5 <- max(map_tmp$V5) - map_tmp$V5 - map_tmp$V6 <- max(map_tmp$V6) - map_tmp$V6 - } - if (index == 1) { - plot(map_tmp$V2, map_tmp$V5, xlab = "Position (Mb)", ylab = "Recombination Distance (cM)", xaxt = "n", main = paste0("LG", lg), col = alpha(cl[index], 0.3), pch = 20, cex = 1.0, ylim = c(0, ymax)) - points(map_tmp$V2, map_tmp$V6, col = alpha(cl[index], 0.3), pch = 20, cex = 1.0) - arrows(map_tmp$V2, map_tmp$V5, map_tmp$V2, map_tmp$V6, length = 0.0, col = alpha(cl[index], 0.5), lwd = 1.5) - } - else { - points(map_tmp$V2, map_tmp$V5, col = alpha(cl[index], 0.3), pch = 20, cex = 1.0) - points(map_tmp$V2, map_tmp$V6, col = alpha(cl[index], 0.3), pch = 20, cex = 1.0) - arrows(map_tmp$V2, map_tmp$V5, map_tmp$V2, map_tmp$V6, length = 0.0, col = alpha(cl[index], 0.5), lwd = 1.5) - } - index <- index + 1 - } - - agp <- read.table(paste0(normalizePath(args[2]), "/chr.", lg, ".agp")) - agp <- agp[agp$V1 == paste0("LG", lg) & agp$V5 == "W", c(1:3)] - - segments(agp$V2, 2 * c(1:2), agp$V3, 2 * c(1:2), col = c("red", "blue"), lwd = 2, lend = 1) - axis(1, seq(0, 500000000, 5000000), seq(0, 500, 5)) - segments(agp$V2, 0, agp$V2, ymax, lwd = 0.5, col = rgb(0.5, 0.5, 0.5, 0.2)) - dev.off() -} \ No newline at end of file +map=read.table(gzfile("marey.data.gz")) + +NN=unique(map$V3) +MM=unique(map$V4) + +cl=rainbow(length(MM)) + + +for(lg in NN) { + print(lg) + png(paste0("marey", lg, ".png"),width=1024,height=1024) + ymax = 0 + xmax = 0 + for (m in MM) { + map_tmp = map[map$V1==paste0("LG",lg) & map$V3==lg & map$V4==m,] + if (nrow(map_tmp) > 0) { + ymax = max(ymax, max(map_tmp$V5)) + xmax = max(xmax, max(map_tmp$V2)) + } + } + + index = 1 + for (m in MM) { + map_tmp = map[map$V1==paste0("LG",lg) & map$V3==lg & map$V4==m,] + if (nrow(map_tmp) > 0) { + orientation=glm(map_tmp$V2 ~ map_tmp$V5)$coefficients[2] + if (!is.na(orientation) && orientation < 0) { + map_tmp$V5 = max(map_tmp$V6) - map_tmp$V5 + map_tmp$V6 = max(map_tmp$V6) - map_tmp$V6 + } + } + if (index == 1) { + plot(map_tmp$V2, map_tmp$V5, xlab="Position (Mb)",ylab="Recombination Distance",xaxt="n", main=paste0("LG", lg), col=alpha(cl[index],0.3), pch=20, cex=1.0, ylim=c(0,ymax), xlim=c(0,xmax)) + points(map_tmp$V2, map_tmp$V6, col=alpha(cl[index],0.3), pch=20, cex=1.0) + arrows(map_tmp$V2, map_tmp$V5, map_tmp$V2, map_tmp$V6, length=0.0, col=alpha(cl[index],0.5),lwd=1.5) + } + else { + points(map_tmp$V2, map_tmp$V5, col=alpha(cl[index],0.3), pch=20, cex=1.0) + points(map_tmp$V2, map_tmp$V6, col=alpha(cl[index],0.3), pch=20, cex=1.0) + arrows(map_tmp$V2, map_tmp$V5, map_tmp$V2, map_tmp$V6, length=0.0, col=alpha(cl[index],0.5),lwd=1.5) + } + index = index + 1 + } + + agp <- read.table(paste0("chr", lg, ".agp")) + agp=agp[agp$V1==paste0("LG",lg) & agp$V5=="W",c(1:3)] + + segments(agp$V2,2*c(1:2),agp$V3,2*c(1:2),col=c("red","blue"),lwd=2,lend=1) + axis(1,seq(0,500000000,5000000),seq(0,500,5)) + segments(agp$V2,0,agp$V2,ymax,lwd=0.5,col=rgb(0.5,0.5,0.5,0.2)) + dev.off() +} + diff --git a/software/LepAnchor/scripts/prox10x.awk b/software/LepAnchor/scripts/prox10x.awk new file mode 100644 index 0000000..e39c363 --- /dev/null +++ b/software/LepAnchor/scripts/prox10x.awk @@ -0,0 +1,50 @@ +#samtools view -f 0x40 -q 20 5148.bam|awk -f umi.awk|awk -vbin=10000 -f prox10x.awk +#low memory version +BEGIN{ + if (bin == "") + bin = 10000 + ibin = 1.0 / bin + if (minD == "") + minD = 2 + +} +function toBin(p) { + return 1 + bin * int((p - 1) * ibin) +} +{ + p = toBin($2) + i = $1 "\t" p "\t" $3 + if (!(i in used)) { + snpUmi[$3,++numUmi[$3]] = $1"\t"p + used[i] + } + if (!($1 in contigs)) { + print "processing contig " $1 > "/dev/stderr" + contigs[$1]=$2 + } + else if ($2 > contigs[$1]) + contigs[$1]=$2 + +} +END{ + for (u in numUmi) + for (i = 1; i <= numUmi[u]; ++i) + umi[snpUmi[u,i], ++count[snpUmi[u,i]]] = u + + for (c in contigs) { + for (p = 1; p <= contigs[c]; p+=bin) { + delete dist + s1 = c "\t" p + for (i = 1; i <= count[s1]; ++i) { + u = umi[s1, i] + for (j = 1; j <= numUmi[u]; ++j) { + s2 = snpUmi[u,j] + ++dist[s1"\t"s2] + } + } + for (d in dist) + if (dist[d] >= minD) + print d "\t" dist[d] + } + } +} diff --git a/software/LepAnchor/scripts/removeHaplotypes.awk b/software/LepAnchor/scripts/removeHaplotypes.awk index a3d6510..7867095 100644 --- a/software/LepAnchor/scripts/removeHaplotypes.awk +++ b/software/LepAnchor/scripts/removeHaplotypes.awk @@ -7,7 +7,7 @@ #chr*.err are the output from Lep-Anchor error stream, e.g. java PlaceAndOrientContigs ... chromosome=X ... 2>chrX.err #haplotypes are put to maxChr+1 BEGIN{ - FS="\t" +# FS="\t" OFS="\t" } @@ -74,9 +74,10 @@ BEGIN{ bed[contig, jmax + 1] = $0 } } + ++numHaplotypes } END{ - if (jmax != "") + if (jmax != "" || numHaplotypes == 0) for (i in contigs) { for (j = 1; j <= c[i]; ++j) print bed[i, j] diff --git a/software/LepAnchor/scripts/sortpaf.awk b/software/LepAnchor/scripts/sortpaf.awk new file mode 100644 index 0000000..ee2c38a --- /dev/null +++ b/software/LepAnchor/scripts/sortpaf.awk @@ -0,0 +1,7 @@ +{ + for (j = 13; j <= NF; ++j) + if ($j ~ /^AS:i:/) { + print (substr($j, 6)+0) "\t" $0 + break + } +} diff --git a/software/LepAnchor/scripts/umi.awk b/software/LepAnchor/scripts/umi.awk new file mode 100644 index 0000000..45d0b15 --- /dev/null +++ b/software/LepAnchor/scripts/umi.awk @@ -0,0 +1,64 @@ +#samtools view -f 0x40 10x.bam|awk -f umi.awk +#10x.bam: bwa mem -t 16 REF R1.fq.gz R2.fq.gz|samtools view -b -|samtools sort - -@4 -T tmp1 -o 10x.bam +#get 10x barcodes from bam aligning raw reads (without cutting barcode off) +#process 10x data for Lep-Anchor +BEGIN{ + #store possible cigar codes for soft clipping of barcode (16bp + 7bp), 16..24 should work + for (i = 16; i<=24; ++i) + s[i "S"] +} +function reverseComplement(s ,i,n,map,ret,nc) { + n = length(s) + map["A"]="T" + map["C"]="G" + map["G"]="C" + map["T"]="A" + map["Y"]="R" + map["R"]="Y" + map["S"]="S" + map["W"]="W" + map["K"]="M" + map["M"]="K" + map["B"]="V" + map["V"]="B" + map["D"]="H" + map["H"]="D" + map["N"]="N" + + map["a"]="t" + map["c"]="g" + map["g"]="c" + map["t"]="a" + map["y"]="r" + map["r"]="y" + map["s"]="s" + map["w"]="w" + map["k"]="m" + map["m"]="k" + map["b"]="v" + map["v"]="b" + map["d"]="h" + map["h"]="d" + map["n"]="n" + + ret = "" + for (i = n; i >= 1; --i) { + nc = map[substr(s, i, 1)] + if (nc == "") { + print "Error, letter " substr(s, i, 1) " in fasta" + exit + } + ret = ret nc + } + return ret +} + + +(and($2, 0x40)!=0){ + if (and($2, 0x10)==0) { #+ orientation + if (substr($6,1,3) in s) + print $3"\t"$4"\t"substr($10,1,16) "\t+" + } + else if (substr($6,length($6)-2) in s) #-orientation + print $3"\t"$4"\t"reverseComplement(substr($10,length($10)-15)) "\t-" +} diff --git a/software/LepAnchor/src/CleanMap.java b/software/LepAnchor/src/CleanMap.java deleted file mode 100644 index 887c28f..0000000 --- a/software/LepAnchor/src/CleanMap.java +++ /dev/null @@ -1,559 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ -//User interface and implementation of ScaffoldHMM -import java.io.BufferedReader; -import java.io.FileReader; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; - -public class CleanMap { - - int markerDistance = 50; - int chimericDistance = 1000; - int numChromosomes; - - int numMarkerTypes = 1; // how many different marker types, used for having different error parameters for repeats or different maps... - - double P_CHIMERA = 0.01; - //double P_ERROR = 0.01;//new double[]{0.01}; - double P_ERROR[] = new double[]{0.01}; - - int MAX_ITERATIONS = 20; - - public void setMarkerDistance(int value) - { - markerDistance = value; - } - - public void setChimericDistance(int value) - { - chimericDistance = value; - } - - public void setInitialValues(double p_c, double p_e) - { - for (int type = 0; type < numMarkerTypes; ++type) - P_ERROR[type] = p_e; - //P_ERROR = p_e; - P_CHIMERA = p_c; - } - private class CleanMarker { - public CleanMarker(long position, int chr, int density, int type){ - this.position = position; - this.chr = chr; - this.density = density; - this.type = type; - } - - public long getPosition() - { - return position; - } - public int getChr() - { - return chr; - } - public int getDensity() - { - return density; - } - public int getType() - { - return type; - } - public void setDensity(int value) - { - density = value; - } - public void setType(int value) - { - type = value; - } - - long position; - int chr; - int density; - int type; - } - - - public void cleanMap(String mapFile, int annotation) - { - - HashMap> scaffoldMap = new HashMap>(); - - HashMap numAnnotations = new HashMap(); - - numChromosomes = 0; - - int noAnnotation = 0; - - try { - BufferedReader br = new BufferedReader(new FileReader(mapFile)); - - ArrayList line = Input.loadTableRow(br, " \t"); - while (line != null) { - if (!line.get(0).equals("CHR") && !line.get(0).equals("CHROM")) { - String scaffold = line.get(0); - long position = InputData.myParseLong(line.get(1)); - - int chr = Integer.parseInt(line.get(2)); - if (chr > 0) { - if (!scaffoldMap.containsKey(scaffold)) - scaffoldMap.put(scaffold, new ArrayList()); - ArrayList list = scaffoldMap.get(scaffold); - - //load possible annotation... - int annot = 0; - if (annotation > 0 && line.size() >= annotation) { - annot = Integer.parseInt(line.get(annotation - 1)); - if (annot >= 0) { // no negative annotations... - if (!numAnnotations.containsKey(annot)) - numAnnotations.put(annot, 0); - numAnnotations.put(annot, numAnnotations.get(annot) + 1); - } else - ++noAnnotation; - } else - ++noAnnotation; - - list.add(new CleanMarker(position, chr - 1, 1, annot)); - // code chr as 0..(c-1), negative missing - // density = 1, for calculating marker density... - numChromosomes = Math.max(numChromosomes, chr); - } - } - line = Input.loadTableRow(br, " \t"); - } - } catch (Exception e) { - e.printStackTrace(); - System.exit(-1); - } - - - int numScaffolds = scaffoldMap.keySet().size(); -// double markerDensityScales[][] = new double[numScaffolds][]; - ArrayList> positionsAndChrs = new ArrayList>(); - ArrayList scaffolds = new ArrayList(); - for (String scaffold : scaffoldMap.keySet()) - scaffolds.add(scaffold); - - int totalPads = 0; - for (String scaffold : scaffolds) { - ArrayList positionsAndChr = scaffoldMap.get(scaffold); - positionsAndChrs.add(positionsAndChr); - int numM = positionsAndChr.size(); -// markerDensityScales[si] = new double[numM]; - for (int i = 1; i < numM; ++i) { - if (positionsAndChr.get(i - 1).getPosition() > positionsAndChr.get(i).getPosition()) { - System.err.println("physical positions must be increasing..."); - System.exit(0); - } - } - int j = 0; - int k = 1; - for (int i = 0; i < numM; ++i) { - while (positionsAndChr.get(i).getPosition() - positionsAndChr.get(j).getPosition() > markerDistance) - ++j; - while (k < numM && positionsAndChr.get(k).getPosition() - positionsAndChr.get(i).getPosition() <= markerDistance) - ++k; - positionsAndChr.get(i).setDensity(k - j); - //markerDensityScales[si][i] = 1.0 / (k - j); - } - - ArrayList positionsAndChr_padded = new ArrayList(); - - for (int i = 0; i < numM; ++i) { - if (i > 0) { - long prevP = positionsAndChr.get(i - 1).getPosition(); - long pos = positionsAndChr.get(i).getPosition(); - - long numPads = (pos - prevP) / chimericDistance; - if (numPads >= 2) - totalPads += numPads - 1; - for (j = 1; j < numPads; ++j) { - double p = ((double) j) / numPads; - // chr missing - // density is 1 - positionsAndChr_padded.add(new CleanMarker((long)(pos + p * (prevP - pos)), -1, 1, 0)); - } - } - positionsAndChr_padded.add(positionsAndChr.get(i)); - } - positionsAndChr.clear(); - positionsAndChr.addAll(positionsAndChr_padded); - - } - System.err.println("Number of scaffolds = " + numScaffolds); - System.err.println("Number of chromosomes = " + numChromosomes); - - System.err.println("Number of padding markers = " + totalPads); - - System.err.println("Number of markers without annotation = " + noAnnotation); - if (numAnnotations.size() > 0) { - for (int key : numAnnotations.keySet()) { - numMarkerTypes = Math.max(numMarkerTypes, key + 1); - System.err.println("Number of markers with annotation " + key + " = " + numAnnotations.get(key)); - } - } - - DecimalFormat df = new DecimalFormat("#0.000"); - - for (int iterations = 0; iterations <= MAX_ITERATIONS; ++iterations) { - initParameters(); - - double QE[][] = new double[numMarkerTypes][2]; -// double QE[] = new double[2]; - double QT[] = new double[2]; - double logL = 0.0; - double l = 1.0; - for (int scaffold=0; scaffold < numScaffolds; ++scaffold) { - ArrayList positionsAndChr = positionsAndChrs.get(scaffold); -// double markerDensityScale[] = markerDensityScales[scaffold]; - int numM = positionsAndChr.size(); - double forward[][] = new double[numM + 1][numChromosomes]; - - for (int j = 0; j < numChromosomes; ++j) - forward[0][j] = 1.0 / numChromosomes; - - long prevPos = 0; - double scale[] = new double[numM + 1]; - scale[0] = 1.0; - - for (int i = 0; i < numM; ++i) { - CleanMarker m = positionsAndChr.get(i); - long pos = m.getPosition(); - int chr = m.getChr(); - int density = m.getDensity(); - int type = m.getType(); - - double p1 = 1.0; - if (i > 0) - p1 = transition((int)(pos - prevPos)); - - double p2 = (1.0 - p1) / numChromosomes; - - double sum = 0.0; - for (int j = 0; j < numChromosomes; ++j) - sum += forward[i][j]; - double sump2 = p2 * sum; - - for (int j = 0; j < numChromosomes; ++j) - forward[i + 1][j] = (sump2 + p1 * forward[i][j]) * emission(chr, j, density, type); - - sum = 0; - for (int j = 0; j < numChromosomes; ++j) - sum += forward[i + 1][j]; - double isum = 1.0 / sum; - - for (int j = 0; j < numChromosomes; ++j) - forward[i + 1][j] *= isum; - scale[i + 1] = isum; - - l *= sum; - if (l < 1e-100) { - logL += Math.log10(l); - l = 1.0; - } - - prevPos = pos; - } - - //backward - double backward[][] = new double[numM + 1][numChromosomes]; - for (int j = 0; j < numChromosomes; ++j) - backward[numM][j] = 1.0; - - for (int i = numM - 1; i >= 0; --i) { - CleanMarker m = positionsAndChr.get(i); - long pos = m.getPosition(); - int chr = m.getChr(); - int density = m.getDensity(); - int type = m.getType(); - double p1 = 1.0; - if (i > 0) { - prevPos = positionsAndChr.get(i - 1).getPosition(); - p1 = transition(pos - prevPos); - } - double p2 = (1.0 - p1) / numChromosomes; - - double sum = 0.0; - for (int j = 0; j < numChromosomes; ++j) - sum += backward[i + 1][j]; - - double sump2 = sum * p2; - - for (int j = 0; j < numChromosomes; ++j) - backward[i][j] = (sump2 + p1 * backward[i + 1][j]) * scale[i + 1] * emission(chr, j, density, type); - } - //for (int j = 0; j < numChromosomes; ++j) { - // System.err.println(backward[0][j]); - //} - - //QE - for (int i = 0; i < numM; ++i) { - CleanMarker m = positionsAndChr.get(i); - int chr = m.getChr(); - int density = m.getDensity(); - int type = m.type; - for (int j = 0; j < numChromosomes; ++j) { - if (chr < 0) { - //QE[0] += ? - //QE[1] += ? - } else { - if (chr == j) - QE[type][0] += backward[i + 1][j] * forward[i + 1][j] / density; - else - QE[type][1] += backward[i + 1][j] * forward[i + 1][j] / density; - } - } - } - - //QT - for (int i = 0; i < numM; ++i) { - CleanMarker m = positionsAndChr.get(i); - long pos = m.getPosition(); - if (i > 0) { - int chr = m.getChr(); - int density = m.getDensity(); - int type = m.getType(); - - double sum = 0.0; - for (int j = 0; j < numChromosomes; ++j) - sum += backward[i + 1][j]; - - double p1 = transition(pos - prevPos); - double p2 = (1.0 - p1) / numChromosomes; - //if (ts != 1.0) - // System.err.println(ts); - for (int j = 0; j < numChromosomes; ++j) { - - double se = scale[i + 1] * emission(chr, j, density, type); - QT[0] += p1 * forward[i][j] * backward[i + 1][j] * se; - QT[1] += p2 * forward[i][j] * sum * se; - } - - } - prevPos = pos; - } - - if (iterations == MAX_ITERATIONS) { - // Viterbi - double viterbi[][] = new double[numM + 1][numChromosomes]; - int path[][] = new int[numM + 1][numChromosomes]; - - for (int j = 0; j < numChromosomes; ++j) - viterbi[0][j] = 1.0 / numChromosomes; - - prevPos = 0; - - for (int i = 0; i < numM; ++i) { - CleanMarker m = positionsAndChr.get(i); - long pos = m.getPosition(); - int chr = m.getChr(); - int density = m.getDensity(); - int type = m.getType(); - - double p1 = 1.0; - if (i > 0) - p1 = transition((int)(pos - prevPos)); - - double p2 = (1.0 - p1) / numChromosomes; - - int maxj = 0; - for (int j = 1; j < numChromosomes; ++j) - if (viterbi[i][j] > viterbi[i][maxj]) - maxj = j; - - double sump2 = p2 * viterbi[i][maxj]; - - for (int j = 0; j < numChromosomes; ++j) { - double sump1 = p1 * viterbi[i][j]; - if (sump2 > sump1) { - viterbi[i + 1][j] = sump2 * emission(chr, j, density, type); - path[i + 1][j] = maxj; - } - else { - viterbi[i + 1][j] = sump1 * emission(chr, j, density, type); - path[i + 1][j] = j; - } - } - - double max = 0.0; - for (int j = 0; j < numChromosomes; ++j) - max = Math.max(max, viterbi[i + 1][j]); - - double imax = 1.0 / max; - for (int j = 0; j < numChromosomes; ++j) - viterbi[i + 1][j] *= imax; - - prevPos = pos; - } - //backtrack path - int finalPath[] = new int[numM]; - - int maxj = 0; - for (int j = 1; j < numChromosomes; ++j) - if (viterbi[numM][j] > viterbi[numM][maxj]) - maxj = j; - - for (int i = numM; i >= 1; --i) { - finalPath[i - 1] = maxj; - maxj = path[i][maxj]; - } - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < numM; ++i) { - /*int maxJ = 0; - for (int j = 1; j < numChromosomes; ++j) { - if (backward[i + 1][j] * forward[i + 1][j] > backward[i + 1][maxJ] * forward[i + 1][maxJ]) - maxJ = j; - }*/ - int maxJ = finalPath[i]; // Viterbi path - double max2 = Double.NEGATIVE_INFINITY; - for (int j = 0; j < numChromosomes; ++j) { - if (j != maxJ) { - max2 = Math.max(max2, backward[i + 1][j] * forward[i + 1][j]); - } - } - max2 = Math.log10(backward[i + 1][maxJ] * forward[i + 1][maxJ] / max2); - CleanMarker m = positionsAndChr.get(i); - long pos = m.getPosition(); - int chr = m.getChr(); - if (chr >= 0) {// do not print padding markers - sb.append(scaffolds.get(scaffold)); - sb.append('\t'); - sb.append(pos); - sb.append('\t'); - sb.append((maxJ + 1)); - sb.append('\t'); - sb.append((chr + 1)); - sb.append('\t'); - sb.append(df.format(max2)); - sb.append('\n'); - if (sb.length() >= 10000) { - System.out.print(sb.toString()); - sb.setLength(0); - } - //System.out.println(scaffolds.get(scaffold) + "\t" + pos + "\t" + (maxJ + 1) + "\t" + (chr + 1) + "\t" + max2); - } - } - if (sb.length() > 0) - System.out.print(sb.toString()); - } - //System.err.println(scaffolds.get(scaffold) + "\t" + logScale + "\t" + QE[0] + "\t" + QE[1]); - } - logL = logL + Math.log10(l); - System.err.println("logL = " + logL); - for (int type = 0; type < numMarkerTypes; ++type) - System.err.print(P_ERROR[type] + "\t"); - System.err.println(P_CHIMERA); - for (int type = 0; type < numMarkerTypes; ++type) - P_ERROR[type] = QE[type][1] / (QE[type][0] + QE[type][1]); - P_CHIMERA = QT[1] / (QT[0] + QT[1]); - } - } - - private double transition(long distance) - { - return (1 - P_CHIMERA); - } - - private double emissionTable1[][] = null; - private double emissionTable2[][] = null; - - private void initParameters() - { - if (P_ERROR.length == 1 && numMarkerTypes > 1) { - double oldValue = P_ERROR[0]; - P_ERROR = new double[numMarkerTypes]; - Arrays.fill(P_ERROR, oldValue); - } - if (emissionTable1 == null) { - emissionTable1 = new double[numMarkerTypes][1000]; - emissionTable2 = new double[numMarkerTypes][1000]; - } - - for (int type = 0; type < numMarkerTypes; ++type) { - double e = P_ERROR[type] / (double) (numChromosomes - 1); // P_ERROR/0 does not matter! - for (int i = 0; i < emissionTable1[type].length; ++i) { - emissionTable1[type][i] = Math.pow(e, 1.0 / i); - emissionTable2[type][i] = Math.pow(1.0 - P_ERROR[type], 1.0 / i); - } - } - - } - - private double emission(int chr, int state, int density, int type){ - if (chr < 0) - return 1; - - assert(density >= 1); - - if (density < emissionTable1[type].length) { - if (chr != state) - return emissionTable1[type][density]; - else - return emissionTable2[type][density]; - - } - return Math.pow(emission(chr, state, 1, type), 1.0 / density); - } - - private static void usage() - { - System.err.println("Usage: java CleanMap map=contig_pos_map.txt [options]"); - System.err.println(" map=file a map file containing columns contig, position and chromosome"); - System.err.println(" markerDistance=NUM Minimum effective distance of markers [50]"); - System.err.println(" chimericDistance=NUM The default distance of markers for transition (change of chromosome) [1000]"); - System.err.println(" initialValues=NUM NUM Initial values for theta and epsilon [0.01 0.01]"); - - System.err.println(" markerAnnotation=NUM Load marker annotation from input map column NUM"); - } - - - public static void main(String args[]) - { - ParameterParser pp = new ParameterParser(); - - String extraParameters = ""; - for (int i = 0; i < args.length; ++i) { - extraParameters += " " + args[i]; - } - - if (args.length == 0 || !pp.init(extraParameters)) { - usage(); - System.exit(0); - } - pp.warning(new String[]{"map", "markerDistance", "chimericDistance", "initValues", "markerAnnotation"}); - - - System.out.println("#java CleanMap" + extraParameters); - - CleanMap cm = new CleanMap(); - cm.setChimericDistance(Integer.parseInt(pp.getValueAsString("chimericDistance", "1000"))); - cm.setMarkerDistance(Integer.parseInt(pp.getValueAsString("markerDistance", "50"))); - cm.setInitialValues(Double.parseDouble(pp.getValueAsString("initialValues",1,"0.01")), Double.parseDouble(pp.getValueAsString("initialValues", 2,"0.01"))); - - cm.cleanMap(pp.getValueAsString("map", null), Integer.parseInt(pp.getValueAsString("markerAnnotation", "-1"))); - } - -} diff --git a/software/LepAnchor/src/ContigInterval.java b/software/LepAnchor/src/ContigInterval.java deleted file mode 100644 index 2a84d45..0000000 --- a/software/LepAnchor/src/ContigInterval.java +++ /dev/null @@ -1,382 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ -import java.util.*; - -public class ContigInterval implements Comparable{ - private String name; - private long start; - private long end; - - private long startI[]; - private long endI[]; - - private int chr; - - private int currentOrientation = 0; - private int rank = 0; - - public ArrayList> markers = new ArrayList>(); - - public void setRank(int rank) { - this.rank = rank; - } - - public String getContig() - { - return name; - } - - public int getChromosome() - { - return chr; - } - public void setChromosome(int value) - { - chr = value; - } - - public long getMinStart() { - if (startI != null) - return startI[0]; - return start; - } - public long getMaxStart() { - if (startI != null) - return startI[1]; - return start; - } - - public long getMaxEnd() { - if (endI != null) - return endI[1]; - return end; - } - public long getMinEnd() { - if (endI != null) - return endI[0]; - return end; - } - - public long getStart() { - return start; - } - - public long[] getStartI() - { - long[] ret = new long[2]; - ret[0] = getMinStart(); - ret[1] = getMaxStart(); - return ret; - } - - public long[] getEndI() - { - if (endI == null) - return new long[]{end, end}; - - long[] ret = new long[endI.length]; - ret[0] = getMinEnd(); - ret[1] = getMaxEnd(); - return ret; - } - - - public void setStart(long value) { - start = value; - } - - public long getEnd() { - return end; - } - - public void setEnd(long value) { - end = value; - } - - public int getRank(){ - return rank; - } - public void setOrientation(int o) { - this.currentOrientation = o; - } - public int getOrientation(){ - return currentOrientation; - } - public void flipOrientation() - { - if (currentOrientation == 0) - currentOrientation = -1; - else - currentOrientation = -currentOrientation; - } - //copy ContigInterval - public ContigInterval(ContigInterval c) { - this.name = c.name; - this.start = c.start; - this.end = c.end; - - //TODO: consider Arrays.copyOf - this.startI = c.startI; - this.endI = c.endI; - - this.chr = c.chr; - this.currentOrientation = c.currentOrientation; - this.rank = c.rank; - - this.markers = new ArrayList>(); - for (ArrayList mm: c.markers) { - ArrayList mm_new = new ArrayList(); - this.markers.add(mm_new); - for (Marker m : mm) - mm_new.add(new Marker(m, this)); - } - } - - public ContigInterval(String name, long start, long end) - { - this.name = name; - this.start = start; - this.end = end; - } - - public ContigInterval(String name, long start[], long end[]) - { - this.name = name; - if (start[0] == 1) - this.start = 1; - else - this.start = (start[0] + start[1]) / 2; - - if (end.length > 2) - this.end = end[1]; - else - this.end = (end[0] + end[1]) / 2; - startI = start; - endI = end; - } - - public boolean inside(long p) - { - if (p >= start && p <= end) - return true; - return false; - } - @Override - public int hashCode() { - return name.hashCode() + Long.hashCode(start); - } - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null) - return false; - if (this.getClass() != o.getClass()) - return false; - - ContigInterval cio = (ContigInterval) o; - return (name.equals(cio.name) && start == cio.start); - } - @Override - public int compareTo(ContigInterval o) { - int strcmp = name.compareTo(o.name); - if (strcmp != 0) - return strcmp; - - if (start < o.start) - return -1; - if (start > o.start) - return 1; - return 0; - } - @Override - public String toString() - { - return name + "\t" + start + "\t" + end; - } - public void addMap() - { - markers.add(new ArrayList()); - } - public void addMarker(Marker m, int map) - { - while (markers.size() <= map) - addMap(); - markers.get(map).add(m); - - if (chr > 0 && chr != m.getChromosome()) { - System.err.println(this); - System.err.println("Error: Multiple chromosomes in one interval in bed"); - System.exit(-1); - } - chr = m.getChromosome(); - m.ci = this; - } - public int getNumMarkers() - { - int ret = 0; - for (ArrayList m : markers) - ret += m.size(); - return ret; - } - - public ArrayList getMarkers(int map) { - if (markers.size() > map) { - if (currentOrientation >= 0) { - ArrayList ret = new ArrayList(); - ret.addAll(markers.get(map)); - return ret; - } - else { - ArrayList ret = new ArrayList(); - ret.addAll(markers.get(map)); - Collections.reverse(ret); - return ret; - } - } else - return new ArrayList(); - } - public ArrayList getMarkersReverse(int map) { - return getMarkers(map, (currentOrientation >= 0) ? -1 : 0); - } - - - public ArrayList getMarkers(int map, int orientation) { - if (markers.size() > map) { - if (orientation == 0) { - ArrayList ret = new ArrayList(); - ret.addAll(markers.get(map)); - return ret; - } - else { - ArrayList ret = new ArrayList(); - ret.addAll(markers.get(map)); - Collections.reverse(ret); - return ret; - } - } else - return new ArrayList(); - } - - public Marker getMarker(int map, int index) { - return markers.get(map).get(index); - } - - //calculate gap length for - // ci in orientation -> c2 with alignment [astart to aend] - public long calculateGapLength(int orientation, long astart, long aend){ - long l = 0; - if (orientation == 0) { // + orientation - if (astart > getEnd()) - l = astart - getEnd() - 1; - else if (aend < getEnd()) - l = getEnd() - aend; - } else { // - orientation - if (aend < getStart()) - l = getStart() - aend - 1; - else if (astart > getStart()) - l = astart - getStart(); - } - return l; - } - - //ci is the haplotype, calculate how much of it does not align... - public long calculateGapLengthHaplotype(long astart, long aend){ - - long g1 = Math.max(0, astart - getStart()); // getStart() - long g2 = Math.max(0, getEnd() - aend); // getEnd() - - return g1 + g2; - } - - //split ContigInterval (this) to start..position and position+1,...end - public ContigInterval[] splitContigInterval(long position[]) - { - if (position[0] < start || position[1] >= end) // no need to split - return new ContigInterval[]{this}; - - long pos = (position[0] + position[1]) / 2; - - ContigInterval c1 = new ContigInterval(name, new long[]{getMinStart(), getMaxStart()}, position); - ContigInterval c2 = new ContigInterval(name, new long[]{position[0] + 1, position[1] + 1}, new long[]{getMinEnd(), getMaxEnd()}); - int numMaps = markers.size(); - - for (int map = 0; map < numMaps; ++map) { - ArrayList list = markers.get(map); - for (Marker m : list) { - if (m.getPosition() > pos) { - Marker mNew = new Marker(m); - mNew.ci = c2; - c2.addMarker(mNew, map); - } else { - Marker mNew = new Marker(m); - mNew.ci = c1; - c1.addMarker(mNew, map); - } - } - } - if (c1.getNumMarkers() == 0 || c2.getNumMarkers() == 0) - return new ContigInterval[]{this}; - - return new ContigInterval[]{c1, c2}; - } - public ContigInterval[] splitContigInterval(long position1[], long position2[], long position3[]) - { - if (position1[0] > position2[0]) { - long tmp[] = position1; - position1 = position2; - position2 = tmp; - } - if (position1[0] > position3[0]) { - long tmp[] = position1; - position1 = position3; - position3 = tmp; - } - ContigInterval sp1[] = splitContigInterval(position2, position3); - ContigInterval sp2[] = sp1[0].splitContigInterval(position1); - ArrayList list = new ArrayList(); - for (int i = 0; i < sp2.length; ++i) - list.add(sp2[i]); - for (int i = 1; i < sp1.length; ++i) - list.add(sp1[i]); - ContigInterval s[] = new ContigInterval[list.size()]; - return list.toArray(s); - - } - - public ContigInterval[] splitContigInterval(long position1[], long position2[]) - { - if (position1[0] > position2[0]) { - long tmp[] = position1; - position1 = position2; - position2 = tmp; - } - ContigInterval sp1[] = splitContigInterval(position1); - if (sp1.length == 2) { - ContigInterval sp2[] = sp1[1].splitContigInterval(position2); - if (sp2.length == 2) - return new ContigInterval[]{sp1[0], sp2[0], sp2[1]}; - else - return sp1; - } else - return splitContigInterval(position2); - } - -} diff --git a/software/LepAnchor/src/CoverageAnalyser.java b/software/LepAnchor/src/CoverageAnalyser.java deleted file mode 100644 index 40368a0..0000000 --- a/software/LepAnchor/src/CoverageAnalyser.java +++ /dev/null @@ -1,420 +0,0 @@ -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.HashMap; - -public class CoverageAnalyser { - - // private HashMap> posDepth = new HashMap>(); - private int column = 2; - private int numCategories = 2; - - private double normalDepth = 100.0; - - private int maxDepth = 1000; - private double zetaK = 1.5; - - private int MAX_ITERATIONS = 100; - - private long[] count = null; - - private double[] freq = new double[3]; - - boolean findNormal = false; - - // private ArrayList variance = new ArrayList(); - - /* - * public void loadDepth(String fn) { try { BufferedReader br = null; if - * (fn.equals("-")) br = new BufferedReader(new - * InputStreamReader(System.in)); else br = new BufferedReader(new - * FileReader(fn)); - * - * do { ArrayList row = Input.loadTableRow(br, "[\t ]"); if (row == - * null) break; if (row.size() <= column) { - * System.err.println("Warning: skipping " + row); continue; } - * - * String contig = row.get(0); ArrayList list = posDepth.get(contig); - * if (list == null) { list = new ArrayList(); posDepth.put(contig, - * list); } //only store last position with same depth... int lsize = - * list.size(); long cov = 0; if (lsize == 0) { cov = - * Long.parseLong(row.get(1)) list.add(cov); // genomic position - * list.add(Long.parseLong(row.get(column))); // depth } else { long lastcov - * = list.get(lsize - 1); long cov = Long.parseLong(row.get(column)); if - * (lastcov == cov) { list.set(lsize - 2, Long.parseLong(row.get(1))); // - * update position } else { list.add(Long.parseLong(row.get(1))); // genomic - * position list.add(cov); // depth } } } while (true); br.close(); } catch - * (Exception e) { e.printStackTrace(); System.err.println("Error in file " - * + fn); System.exit(-1); } } - */ - - public void loadDepth(String fn) { - count = new long[maxDepth + 2]; - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - do { - ArrayList row = Input.loadTableRow(br, "[\t ]"); - if (row == null) - break; - if (row.size() <= column) { - System.err.println("Warning: skipping " + row); - continue; - } - int depth = Integer.parseInt(row.get(column)); - if (depth > maxDepth) - depth = maxDepth + 1; - ++count[depth]; - } while (true); - br.close(); - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - System.exit(-1); - } - } - - public static final double isqrtPI = 1.0 / Math.sqrt(Math.PI); - public static final double isqrt2 = 1.0 / Math.sqrt(2.0); - - private class Gaussian { - private double mean; - private double stdev; - private double istdev; - private double istdev2sp; - private double logIstdev2sp; - - // Misc.i2sqrtPI = 1.0 / Math.sqrt(2.0 * Math.PI); - - public Gaussian(double mean, double stdev) { - istdev = isqrt2 / stdev; - istdev2sp = istdev * isqrtPI; - logIstdev2sp = Math.log(istdev2sp); - this.mean = mean; - this.stdev = stdev; - } - public double getMean() - { - return mean; - } - - public double getStdev() - { - return stdev; - } - - public double density(double x) { - double tmp = (x - mean) * istdev; - return istdev2sp * Math.exp(-tmp * tmp); - } - - public double logDensity(double x) { - double tmp = (x - mean) * istdev; - return logIstdev2sp - tmp * tmp; - } - } - - //distribution zeta - 1 - private class Zeta { - //double k; - int max; - ArrayList d = new ArrayList(); - //ArrayList ld = new ArrayList(); - public Zeta(double k, int max) { - //this.k = k; - this.max = max; - double sum = 0.0; - for (int i = 0; i <= max; ++i) { - double p = Math.pow(i + 1, -k); - sum += p; - d.add(p); - } - double iSum = 1.0 / sum; - for (int i = 0; i <= max; ++i) - d.set(i, d.get(i) * iSum); - } - public double density(int x) { - if (x > max) - return 0.0; - return d.get(x); - } - //public double logDensity(int x) { - // return ld.get(x); - //} - } - - private double likelihood(boolean verbose) { - Gaussian g[] = new Gaussian[numCategories]; - - /*double rate1 = count[0] / (double) count[1]; - double rate2 = count[0] / (double) count[2]; - double rate3 = count[0] / (double) count[3]; - double k1 = Math.log(rate1) / Math.log(2.0); - double k2 = Math.log(rate2) / Math.log(3.0); - double k3 = Math.log(rate3) / Math.log(4.0); - k = Math.min(k1, k2, k3); - if (k > 1.0) - ; - else k = 1.0;*/ - //double k = 1.2; - - Zeta z = new Zeta(zetaK, maxDepth); - if (verbose) - System.err.println("Zeta parameter " + zetaK); - - { // Initialisation - for (int j = 0; j < numCategories; ++j) { - double mean = (normalDepth * (j + 1)) / (double) numCategories; - g[j] = new Gaussian(mean, Math.sqrt(normalDepth) * Math.exp(2.0 * Math.random() - 1.0)); - } - double sum = 0.0; - for (int j = 0; j <= numCategories; ++j) { - double r = Math.random(); - freq[j] = r; - sum += r; - } - double iSum = 1.0 / sum; - for (int j = 0; j <= numCategories; ++j) - freq[j] *= iSum; - } - - double d[] = new double[numCategories + 1]; - - double likelihood = 0.0; - for (int iteration = 0; iteration < MAX_ITERATIONS; ++iteration) { - double qFreq[] = new double[numCategories + 1]; - double qVar[] = new double[numCategories]; - double qSum[] = new double[numCategories]; - - //System.err.println(freq[0] + "\t" + freq[1] + "\t" + freq[2]); - likelihood = 0.0; - for (int i = 0; i <= maxDepth; ++i) { - - double sum = 0.0; - for (int j = 0; j <= numCategories; ++j) { - double p = 0.0; - if (j < numCategories) - p = g[j].density(i) * freq[j]; - else - p = z.density(i) * freq[j]; - d[j] = p; - sum += p; - } - //System.err.println("Sum " + sum); - double iSum = 1.0 / sum; - - for (int j = 0; j <= numCategories; ++j) { - double q = d[j] * iSum * count[i]; - qFreq[j] += q; - if (j < numCategories) { - double diff = (i - g[j].mean); - qVar[j] += q * diff * diff; - qSum[j] += q; - } - } - likelihood += Math.log(sum) * count[i]; - } - double sum = 0.0; - for (int j = 0; j <= numCategories; ++j) - sum += qFreq[j]; - //for (int j = 0; j <= numCategories; ++j) - // if (qFreq[j] < 0.1 * sum) - // qFreq[j] = 0.1 * sum; - //sum = 0.0; - //for (int j = 0; j <= numCategories; ++j) - // sum += qFreq[j]; - - double iSum = 1.0 / sum; - for (int j = 0; j <= numCategories; ++j) - freq[j] = qFreq[j] * iSum; - - //variance in gaussians must be non-decreasing - for (int j = numCategories - 1; j >= 1; --j) { - if (qVar[j] * qSum[j - 1] < qVar[j - 1] * qSum[j]) { - double qs = qSum[j] + qSum[j - 1]; - double qv = qVar[j] + qVar[j - 1]; - qSum[j] = qs; - qSum[j - 1] = qs; - qVar[j] = qv; - qVar[j - 1] = qv; - } - } - for (int j = 0; j < numCategories; ++j) { - double sdev = Math.sqrt(qVar[j] / qSum[j]); - if (sdev < 1.0) - sdev = 1.0; - //System.err.println(freq[j] + "\t" + sdev); - g[j] = new Gaussian((normalDepth * (j + 1)) / (double) numCategories, sdev); - //System.err.println("mean=" + g[j].mean); - } - //System.err.println(freq[numCategories]); - // System.err.println(Math.sqrt(iSum * qVar[0]) + "\t" + - // Math.sqrt(iSum * qVar[1]) + "\t" + Math.sqrt(iSum * qVar[2])); - if (verbose) - System.err.println("logL=" + likelihood); - } - long totalCount = 0; - for (int i = 0; i <= maxDepth; ++i) { - totalCount += count[i]; - } - if (verbose) - System.err.println(freq[0] + "\t" + freq[1] + "\t" + g[0].getStdev() + "\t" + g[1].getStdev()); - - for (int i = 0; i <= maxDepth; ++i) { - - double tmp[] = new double[numCategories + 2]; - for (int j = 0; j < numCategories; ++j) - tmp[j] = g[j].density(i); - - //split zeta distribution to 0 (<=normalDepht/numCategories) and repeat (>=normalDepht) - double zf = z.density(i); - if (i <= g[0].mean) - tmp[numCategories] = zf; - else if (i >= g[numCategories - 1].mean) - tmp[numCategories + 1] = zf; - else { - double t = (i - g[0].mean) / (g[numCategories - 1].mean - g[0].mean); - tmp[numCategories] = zf * (1.0 - t); - tmp[numCategories + 1] = zf * t; - } - - double sum = 0.0; - double max = 0.0; - for (int j = 0; j <= numCategories + 1; ++j) { - double p = tmp[j] * freq[Math.min(j, numCategories)]; - sum += p; - max = Math.max(max, tmp[j]); - } - if (verbose) { - System.out.print(i + "\t" + count[i] + "\t" + (totalCount * sum) ); - - double iMax = 1.0 / max; - System.out.print("\t" + tmp[numCategories] * iMax); - for (int j = 0; j <= numCategories + 1; ++j) - if (j != numCategories) - System.out.print("\t" + tmp[j] * iMax); - - System.out.println(); - } - } - - - //System.err.println(num[0] + "\t" + num[1] + "\t" + num[2] + "\t" + num[3]); - return likelihood; - } - - - public void analyse() { - if (findNormal) { - // Find normalDepth and other parameters... - System.err.println("Estimating normal depth parameter..."); - double maxL = Double.NEGATIVE_INFINITY; - int maxNd = 1; - for (int nd = 1; nd + nd <= maxDepth; ++nd) { - normalDepth = nd; - double ll = likelihood(false); - if (ll > maxL) { - System.err.println(nd + "\t" + ll + "\t*"); - maxL = ll; - maxNd = nd; - } else - System.err.println(nd + "\t" + ll); - } - normalDepth = maxNd; - System.err.println("Value " + maxNd + " chosen for normal depth"); - } - likelihood(true); - } - - public void setNumCategories(int value) { - numCategories = value; - freq = new double[numCategories + 1]; - - - for (int i = 0; i <= numCategories; ++i) - freq[i] = Math.random(); - } - - public void setNormalDepth(double value) { - normalDepth = value; - maxDepth = (int)(3 * value + 0.99); - setNumCategories(numCategories); - } - - public void setColumn(int value) { - column = value; - } - - public void setMaxDepth(int value) { - maxDepth = value; - findNormal = true; - } - - public void setZeta(double value) { - zetaK = value; - } - - public static void usageInfo() { -// pp.warning(new String[] { "depth", "numCategories", "normalDepth", -// "column", "sample", "maxDepth", "zeta"}); - System.err.println("Usage: java CoverageAnalyser depth=depth.txt [parameters] "); - System.err.println(" depth=file output from samtools -a depth"); - System.err.println(" numCategories=NUM the ploidy [2]"); - System.err.println(" normalDepth=NUM the normal depth [not set, ML value searched]"); - System.err.println(" maxDepthDepth=NUM search normalDepth between 1..NUM, applies only when normalDepth is not set [1000]"); - System.err.println(" column=NUM Take column NUM from depth file [3]"); - System.err.println(" zeta=NUM Zeta distribution parameter [1.5]"); - - System.exit(0); - } - - public static void main(String args[]) { - ParameterParser pp = new ParameterParser(); - - String extraParameters = ""; - for (int i = 0; i < args.length; ++i) { - extraParameters += " " + args[i]; - } - if (args.length == 0 || !pp.init(extraParameters)) { - usageInfo(); - } - System.out.println("#java CoverageAnalyser " + extraParameters); - - pp.warning(new String[] { "depth", "numCategories", "normalDepth", - "column", "maxDepth", "zeta"}); - - String depthFile = pp.getValueAsString("depth", null); - if (depthFile == null) - usageInfo(); - - CoverageAnalyser ca = new CoverageAnalyser(); - int column = Integer.parseInt(pp.getValueAsString("column", "3")); - ca.setColumn(column - 1); - ca.setNumCategories(Integer.parseInt(pp.getValueAsString( - "numCategories", "2"))); - - String normalD = pp.getValueAsString("normalDepth", null); - if (normalD != null) { - ca.setNormalDepth(Double.parseDouble(normalD)); - //ca.setMaxDepth(); - String md = pp.getValueAsString("maxDepth", null); - if (md != null) - ca.setMaxDepth(Integer.parseInt(md)); - } - else - ca.setMaxDepth(Integer.parseInt(pp.getValueAsString("maxDepth", - "1000"))); - - ca.setZeta(Double.parseDouble(pp.getValueAsString("zeta", "1.5"))); - - ca.loadDepth(depthFile); - ca.analyse(); - - } -} diff --git a/software/LepAnchor/src/CoverageHMM.java b/software/LepAnchor/src/CoverageHMM.java deleted file mode 100644 index a03e111..0000000 --- a/software/LepAnchor/src/CoverageHMM.java +++ /dev/null @@ -1,571 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ -//User interface and implementation of ScaffoldHMM -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.InputStreamReader; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; - -public class CoverageHMM { - - private int column = 2; - private int numMixture = 0; - private int MAX_ITERATIONS = 1000; - private int numScaffolds; - - private double minProb = 0.001; - private double scale = 0.001; - private double sample = 0.1; - - private ArrayList scaffolds = new ArrayList(); - - public void setColumn(int value) - { - column = value; - } - - public void setMinProb(double value) - { - minProb = value; - } - - public void setSample(double value) - { - sample = value; - } - - public void setScale(double value) - { - scale = value; - } - - private ArrayList mixture = new ArrayList(); - private HashMap> scaffoldMap = new HashMap>(); - - public void loadMixture(String mixtureFile) - { - numMixture = 0; - try { - BufferedReader br = new BufferedReader(new FileReader(mixtureFile)); - - ArrayList line = Input.loadTableRow(br, " \t"); - while (line != null) { - if (numMixture == 0) - numMixture = line.size() - 3; - else - if (numMixture != line.size() - 3) { - System.err.println("Error: Different number of columns in the input"); - System.exit(-1); - } - int value = Integer.parseInt(line.get(0)); - if (mixture.size() != value) { - System.err.println("Error: Incorrect mixture file"); - System.exit(-1); - } - double m[] = new double[numMixture]; - for (int i = 0; i < numMixture; ++i) - m[i] = Double.parseDouble(line.get(i + 3)); - mixture.add(m); - line = Input.loadTableRow(br, " \t"); - } - } catch (Exception e) { - e.printStackTrace(); - System.exit(-1); - } - } - - - public void loadDepth(String depthFile) - { - try { - BufferedReader br = null; - if (depthFile.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(depthFile)); - - ArrayList line = Input.loadTableRow(br, " \t"); - double pick = 0.0; - while (line != null) { - if (line.size() <= column) { - System.err.println("Warning: skipping " + line); - continue; - } - pick += sample; - if (pick >= 1.0) { - pick -= 1.0; - String scaffold = line.get(0); - long position = Long.parseLong(line.get(1)); - if (position > 0xffffffffl) { - System.err.println("Error: Too long scaffold"); - System.exit(-1); - } - int cov = Integer.parseInt(line.get(column)); - - if (!scaffoldMap.containsKey(scaffold)) - scaffoldMap.put(scaffold, new ArrayList()); - ArrayList list = scaffoldMap.get(scaffold); - list.add((int) position); - list.add(cov); - } - line = Input.loadTableRow(br, " \t"); - } - } catch (Exception e) { - e.printStackTrace(); - System.exit(-1); - } - - numScaffolds = scaffoldMap.keySet().size(); -// double markerDensityScales[][] = new double[numScaffolds][]; - scaffolds = new ArrayList(); - for (String scaffold : scaffoldMap.keySet()) - scaffolds.add(scaffold); - - for (String scaffold : scaffolds) { - ArrayList positionsAndCov = scaffoldMap.get(scaffold); - int numM = positionsAndCov.size(); - for (int i = 2; i < numM; i+=2) { - if (positionsAndCov.get(i - 2) > positionsAndCov.get(i)) { - System.err.println("physical positions must be increasing..."); - System.exit(0); - } - } - } - System.err.println("Number of scaffolds = " + numScaffolds); - } - - public void hmm() - { - DecimalFormat df = new DecimalFormat("#0.000"); - - for (double m[] : mixture) - for (int i = 0; i < numMixture; ++i) { - double v = m[i]; - if (v < minProb) - m[i] = minProb; - m[i] = Math.pow(m[i], scale); - } - - //Init transition - double t[][] = new double[numMixture][numMixture]; - - for (double tmp[] : t) - Arrays.fill(tmp, 1.0 / numMixture); - - for (int i = 0; i < numMixture; ++i) { - double sum = 0.0; - for (int j = 0; j < numMixture; ++j) { - double r = (i == j) ? 1.0 : 0.1 * scale * Math.random(); - sum += r; - t[i][j] = r; - } - double iSum = 1.0 / sum; - for (int j = 0; j < numMixture; ++j) - t[i][j] *= iSum; - } - - boolean converge = false; - double oldLogL = Double.NEGATIVE_INFINITY; - for (int iterations = 0; iterations <= MAX_ITERATIONS; ++iterations) { - int maxCount = mixture.size() - 1; - double QT[][] = new double[numMixture][numMixture]; - double logL = 0.0; - double l = 1.0; - for (int scaffold=0; scaffold < numScaffolds; ++scaffold) { - String scaffoldS = scaffolds.get(scaffold); - ArrayList positionsAndCov = scaffoldMap.get(scaffoldS); - - int numM = positionsAndCov.size() / 2; - double forward[][] = new double[numM + 1][numMixture]; - double scale[] = new double[numM + 1]; - scale[0] = 1.0; - - double iM = 1.0 / numMixture; - for (int i = 0; i < numMixture; ++i) - forward[0][i] = iM; - - for (int p = 0; p < numM; ++p) { - int cov = positionsAndCov.get(p + p + 1); - if (cov >= maxCount) - cov = maxCount; - - double m[] = mixture.get(cov); - - for (int j = 0; j < numMixture; ++j) { - double fp1j = 0.0; - double f_[] = forward[p]; - for (int i = 0; i < numMixture; ++i) - fp1j += f_[i] * t[i][j]; - forward[p + 1][j] = fp1j * m[j]; - } - - - double sum = 0.0; - for (int j = 0; j < numMixture; ++j) - sum += forward[p + 1][j]; - double isum = 1.0 / sum; - - for (int j = 0; j < numMixture; ++j) - forward[p + 1][j] *= isum; - scale[p + 1] = isum; - - l *= sum; - if (l < 1e-100) { - logL += Math.log10(l); - l = 1.0; - } - } - double backward[][] = new double[numM + 1][numMixture]; - for (int j = 0; j < numMixture; ++j) - backward[numM][j] = 1.0; - - for (int p = numM - 1; p >= 0; --p) { - int cov = positionsAndCov.get(p + p + 1); - if (cov >= maxCount) - cov = maxCount; - - double m[] = mixture.get(cov); - - - for (int j = 0; j < numMixture; ++j) { - double tmp = backward[p + 1][j] * m[j] * scale[p + 1]; - for (int i = 0; i < numMixture; ++i) - backward[p][i] += t[i][j] * tmp; - } - } - //double sum = 0.0; - //for (int j = 0; j < numMixture; ++j) - // sum += backward[1][j] * forward[1][j]; - //System.err.println(sum); - - if (iterations == MAX_ITERATIONS || converge) { - for (int p = 0; p < numM; ++p) { - long pos = positionsAndCov.get(p + p) & 0xffffffffl; - int cov = positionsAndCov.get(p + p + 1); - double max = 0.0; - double max2 = 0.0; - int maxj = 0; - for (int j = 0; j < numMixture; ++j) { - double pj = forward[p][j] * backward[p][j]; - if (pj > max) { - max2 = max; - max = pj; - maxj = j; - } else if (pj > max2) - max2 = pj; - } - StringBuilder sb = new StringBuilder(); - sb.append(scaffoldS); - sb.append('\t'); - sb.append(pos); - sb.append('\t'); - sb.append(cov); - sb.append('\t'); - sb.append(maxj); - sb.append('\t'); - sb.append(df.format(Math.log10(max) - Math.log10(max2))); - System.out.println(sb.toString()); - } - - } else - for (int p = 1; p < numM; ++p) { - int cov = positionsAndCov.get(p + p + 1); - if (cov >= maxCount) - cov = maxCount; - double m[] = mixture.get(cov); - for (int j = 0; j < numMixture; ++j) { - double tmp = scale[p + 1] * backward[p + 1][j] * m[j]; - for (int i = 0; i < numMixture; ++i) - QT[i][j] += forward[p][i] * t[i][j] * tmp; - } - } - - } - if (iterations == MAX_ITERATIONS || converge) - break; - - for (int i = 0; i < numMixture; ++i) { - double sum = 0.0; - for (double q : QT[i]) - sum += q; - double iSum = 1.0 / sum; - for (int j = 0; j < numMixture; ++j) - t[i][j] = QT[i][j] * iSum; - } - - //for (int i = 0; i < numMixture; ++i) { - // for (int j = 0; j < numMixture; ++j) - // System.err.println(i + "\t" + j + "\t" + t[i][j]); - //} - - logL = logL + Math.log10(l); - System.err.println("logL = " + logL); - if (oldLogL > logL - 0.01) - converge = true; - - oldLogL = logL; - } - - -/* - //backward - double backward[][] = new double[numM + 1][numChromosomes]; - for (int j = 0; j < numChromosomes; ++j) - backward[numM][j] = 1.0; - - for (int i = numM - 1; i >= 0; --i) { - long pos = positionsAndChr.get(3 * i); - int chr = positionsAndChr.get(3 * i + 1).intValue(); - int density = positionsAndChr.get(3 * i + 2).intValue(); - double p1 = 1.0; - if (i > 0) { - prevPos = positionsAndChr.get(3 * i - 3); - p1 = transition(pos - prevPos); - } - double p2 = (1.0 - p1) / numChromosomes; - - double sum = 0.0; - for (int j = 0; j < numChromosomes; ++j) - sum += backward[i + 1][j]; - - double sump2 = sum * p2; - - for (int j = 0; j < numChromosomes; ++j) - backward[i][j] = (sump2 + p1 * backward[i + 1][j]) * scale[i + 1] * emission(chr, j, density); - } - //for (int j = 0; j < numChromosomes; ++j) { - // System.err.println(backward[0][j]); - //} - - //QE - for (int i = 0; i < numM; ++i) { - //int pos = positionsAndChr.get(3 * i); - int chr = positionsAndChr.get(3 * i + 1).intValue(); - int density = positionsAndChr.get(3 * i + 2).intValue(); - for (int j = 0; j < numChromosomes; ++j) { - if (chr < 0) { - //QE[0] += ? - //QE[1] += ? - } else { - if (chr == j) - QE[0] += backward[i + 1][j] * forward[i + 1][j] / density; - else - QE[1] += backward[i + 1][j] * forward[i + 1][j] / density; - } - } - } - - //QT - for (int i = 0; i < numM; ++i) { - long pos = positionsAndChr.get(3 * i); - if (i > 0) { - int chr = positionsAndChr.get(3 * i + 1).intValue(); - int density = positionsAndChr.get(3 * i + 2).intValue(); - - double sum = 0.0; - for (int j = 0; j < numChromosomes; ++j) - sum += backward[i + 1][j]; - - double p1 = transition(pos - prevPos); - double p2 = (1.0 - p1) / numChromosomes; - //if (ts != 1.0) - // System.err.println(ts); - for (int j = 0; j < numChromosomes; ++j) { - - double se = scale[i + 1] * emission(chr, j, density); - QT[0] += p1 * forward[i][j] * backward[i + 1][j] * se; - QT[1] += p2 * forward[i][j] * sum * se; - } - - } - prevPos = pos; - } - - if (iterations == MAX_ITERATIONS) { - // Viterbi - double viterbi[][] = new double[numM + 1][numChromosomes]; - int path[][] = new int[numM + 1][numChromosomes]; - - for (int j = 0; j < numChromosomes; ++j) - viterbi[0][j] = 1.0 / numChromosomes; - - prevPos = 0; - - for (int i = 0; i < numM; ++i) { - long pos = positionsAndChr.get(3 * i); - int chr = positionsAndChr.get(3 * i + 1).intValue(); - int density = positionsAndChr.get(3 * i + 2).intValue(); - - double p1 = 1.0; - if (i > 0) - p1 = transition((int)(pos - prevPos)); - - double p2 = (1.0 - p1) / numChromosomes; - - int maxj = 0; - for (int j = 1; j < numChromosomes; ++j) - if (viterbi[i][j] > viterbi[i][maxj]) - maxj = j; - - double sump2 = p2 * viterbi[i][maxj]; - - for (int j = 0; j < numChromosomes; ++j) { - double sump1 = p1 * viterbi[i][j]; - if (sump2 > sump1) { - viterbi[i + 1][j] = sump2 * emission(chr, j, density); - path[i + 1][j] = maxj; - } - else { - viterbi[i + 1][j] = sump1 * emission(chr, j, density);; - path[i + 1][j] = j; - } - } - - double max = 0.0; - for (int j = 0; j < numChromosomes; ++j) - max = Math.max(max, viterbi[i + 1][j]); - - double imax = 1.0 / max; - for (int j = 0; j < numChromosomes; ++j) - viterbi[i + 1][j] *= imax; - - prevPos = pos; - } - //backtrack path - int finalPath[] = new int[numM]; - - int maxj = 0; - for (int j = 1; j < numChromosomes; ++j) - if (viterbi[numM][j] > viterbi[numM][maxj]) - maxj = j; - - for (int i = numM; i >= 1; --i) { - finalPath[i - 1] = maxj; - maxj = path[i][maxj]; - } - - for (int i = 0; i < numM; ++i) { - int maxJ = finalPath[i]; // Viterbi path - double max2 = Double.NEGATIVE_INFINITY; - for (int j = 0; j < numChromosomes; ++j) { - if (j != maxJ) { - max2 = Math.max(max2, backward[i + 1][j] * forward[i + 1][j]); - } - } - max2 = Math.log10(backward[i + 1][maxJ] * forward[i + 1][maxJ]) - Math.log10(max2); - long pos = positionsAndChr.get(3 * i); - int chr = positionsAndChr.get(3 * i + 1).intValue(); - if (chr >= 0) // do not print padding markers - System.out.println(scaffolds.get(scaffold) + "\t" + pos + "\t" + (maxJ + 1) + "\t" + (chr + 1) + "\t" + max2); - } - } - - //System.err.println(scaffolds.get(scaffold) + "\t" + logScale + "\t" + QE[0] + "\t" + QE[1]); - } - logL = logL + Math.log10(l); - System.err.println("logL = " + logL); - System.err.println(P_ERROR + "\t" + P_CHIMERA); - P_ERROR = QE[1] / (QE[0] + QE[1]); - P_CHIMERA = QT[1] / (QT[0] + QT[1]);*/ - } - - private double emissionTable1[] = new double[1000]; - private double emissionTable2[] = new double[1000]; - - private void initParameters() - { - //double e = P_ERROR / (double) (numChromosomes - 1); // P_ERROR/0 does not matter! - for (int i = 0; i < emissionTable1.length; ++i) { - //emissionTable1[i] = Math.pow(e, 1.0 / i); - //emissionTable2[i] = Math.pow(1.0 - P_ERROR, 1.0 / i); - } - - } - - private double emission(int chr, int state, int density){ - if (chr < 0) - return 1; - - assert(density >= 1); - - if (density < emissionTable1.length) { - if (chr != state) - return emissionTable1[density]; - else - return emissionTable2[density]; - - } - return Math.pow(emission(chr, state, 1), 1.0 / density); - } - - private static void usageInfo() - { - System.err.println("Usage: java CoverageHMM depth=depth.txt mixture=mixture.txt [options]"); - System.err.println("options:"); - System.err.println(" depth=file output from samtools -a depth"); - System.err.println(" mixture=file output from CoverageAnalyser"); - System.err.println(" column=NUM Take column NUM from depth file [3]"); - System.err.println(" scale=NUM One position counts this much [0.01]"); - System.err.println(" sample=NUM Take only NUM fraction of data [0.1]"); - System.err.println(" (default scale*sample=0.001, 1 per 1kb)"); - System.err.println(" minProb=NUM One position can be this sure [0.001] (1:1000)"); - } - - - public static void main(String args[]) - { - ParameterParser pp = new ParameterParser(); - - String extraParameters = ""; - for (int i = 0; i < args.length; ++i) { - extraParameters += " " + args[i]; - } - - if (args.length == 0 || !pp.init(extraParameters)) { - usageInfo(); - System.exit(0); - } - pp.warning(new String[]{"depth", "mixture", "column", "scale", "minProb", "sample"}); - - - System.out.println("#java CoverageHMM" + extraParameters); - - int column = Integer.parseInt(pp.getValueAsString("column", "3")); - CoverageHMM ch = new CoverageHMM(); - ch.setColumn(column - 1); - String df = pp.getValueAsString("depth", null); - String mf = pp.getValueAsString("mixture", null); - if (df == null || mf == null) { - usageInfo(); - System.exit(0); - } - ch.setScale(Double.parseDouble(pp.getValueAsString("scale", "0.001"))); - ch.setMinProb(Double.parseDouble(pp.getValueAsString("minProb", "0.001"))); - ch.setSample(Double.parseDouble(pp.getValueAsString("sample", "0.1"))); - - ch.loadMixture(mf); - ch.loadDepth(df); - ch.hmm(); - } -} diff --git a/software/LepAnchor/src/FindContigErrors.java b/software/LepAnchor/src/FindContigErrors.java deleted file mode 100644 index ad1ebdf..0000000 --- a/software/LepAnchor/src/FindContigErrors.java +++ /dev/null @@ -1,557 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ - -import java.io.BufferedReader; - -import java.io.FileReader; -import java.io.InputStreamReader; -import java.util.*; - -//Calculate score over positions of each contig (paf + ?) -//...and to different position... - -public class FindContigErrors { - //calculate coverage for read mapping intervals... - //could be used to find (about) exact cut positions - - //prox, use borderScore - //map, - - private void addPositions(String contig, HashMap> positionHash, long start, long end, long weight) - { - ArrayList list = positionHash.get(contig); - if (list == null) { - list = new ArrayList(); - positionHash.put(contig, list); - } - list.add(new long[]{start, end, weight}); - } - - ArrayList contigs = new ArrayList(); - ArrayList contigLengths = new ArrayList(); - - HashMap contigHash = new HashMap(); - - private void addContig(String name, long length){ - if (!contigHash.containsKey(name)) { - contigHash.put(name, contigs.size()); - contigs.add(name); - contigLengths.add(length); - } - } - - //TODO: is this needed? Could we use cov instead? - private void mergeCovs(String contig, ArrayList cov, ArrayList cov2) { - System.out.println(contig); - int covi = 0; - int cov2i = 0; - //merge in cov and cov2 - - long pos = 0; - long cp = 0; - long cp2 = 0; - while (covi < cov.size() || cov2i < cov2.size()) { - if (covi >= cov.size() ) { - pos = cov2.get(cov2i); - cp2 = cov2.get(cov2i + 1); - cov2i+=2; - } - else if (cov2i >= cov2.size() ) { - pos = cov.get(covi); - cp = cov.get(covi + 1); - covi+=2; - } - else { - if (cov.get(covi) <= cov2.get(cov2i)) { - pos = cov.get(covi); - cp = cov.get(covi + 1); - covi+=2; - } - else { - pos = cov2.get(cov2i); - cp2 = cov2.get(cov2i + 1); - cov2i+=2; - } - } - System.out.println(pos + "\t" + cp + "\t" + cp2); - } - } - - HashMap> pafPositions = new HashMap>(); - HashMap> pafPositions2 = new HashMap>(); - public void processErrorsFromPaf(String fn) - { - System.err.println("loading paf..."); - ArrayList> rows = new ArrayList>(); - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - do { - ArrayList row = Input.loadTableRow(br, "\t "); - if (row == null) - break; - if (rows.size() == 0 || rows.get(0).get(0).equals(row.get(0))) - rows.add(row); - else { - processPAF(rows); - rows.clear(); - rows.add(row); - } - - } while (true); - processPAF(rows); - - for (String contig : contigs) { - ArrayList list = pafPositions.get(contig); - ArrayList cov = null; - if (list != null) - cov = Misc.cov(list); - else { - cov = new ArrayList(); - cov.add(0L); - cov.add(0L); - } - - ArrayList list2 = pafPositions2.get(contig); - ArrayList cov2 = null; - if (list2 != null) - cov2 = Misc.cov(list2); - else { - cov2 = new ArrayList(); - cov2.add(0L); - cov2.add(0L); - } - - mergeCovs(contig, cov, cov2); - //for (int i = 0; i < cov.size(); i+=2) { - // System.out.println(cov.get(i) + "\t" + cov.get(i + 1)); - //} - } - - } - catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - } - } - - private void processPAF(ArrayList> rows) { - for (ArrayList row : rows) { - String contig = row.get(5); - long length = Long.parseLong(row.get(6)); - addContig(contig, length); - long start = Long.parseLong(row.get(7)) + 1; - long end = Long.parseLong(row.get(8)); - addPositions(contig, pafPositions, start, end, 1); // calculate coverage... - } - //take two longest alignments to two different contigs... - long maxL = 0; - ArrayList maxRow = null; - for (ArrayList row : rows) { - long aLen = Long.parseLong(row.get(8)) - Long.parseLong(row.get(7)); - if (aLen > maxL) { - maxL = aLen; - maxRow = row; - } - } - long maxL2 = 0; - ArrayList maxRow2 = null; - for (ArrayList row : rows) - if (!row.get(5).equals(maxRow.get(5))) { - long aLen = Long.parseLong(row.get(8)) - Long.parseLong(row.get(7)); - if (aLen > maxL2) { - maxL2 = aLen; - maxRow2 = row; - } - } - if (maxRow2 != null) { // two aligments found to two contigs - long rstart1 = Long.parseLong(maxRow.get(2)); - long rstart2 = Long.parseLong(maxRow2.get(2)); - if (rstart1 > rstart2) { // make sure maxRow starts first... - long tmp = rstart2; - rstart2 = rstart1; - rstart1 = tmp; - - tmp = maxL; - maxL = maxL2; - maxL2 = tmp; - - ArrayList tmp2 = maxRow; - maxRow = maxRow2; - maxRow2 = tmp2; - } - long rend1 = Long.parseLong(maxRow.get(3)); - //long rend2 = Long.parseLong(maxRow2.get(3)); - - long distance = rstart2 - rend1 + 2; // - - long minL = Math.min(maxL, maxL2); - - if (distance < -minL || distance > 2 * minL) // no overlap distance 2 x the length of the shorter alignment - return; - if (distance < 0) - distance = 0; -// TODO: figure out what happens if distance < 0, maybe distance can be -2 minL - - String o1 = maxRow.get(4); - if ("+".equals(o1)) { - long end1 = Long.parseLong(maxRow.get(8)); - long length1 = Long.parseLong(maxRow.get(6)); - addPositions(maxRow.get(5), pafPositions2, end1 + 1, Math.min(length1 + 1, end1 + 1 + distance), 1); // calculate coverage... - } else { - long start1 = Long.parseLong(maxRow.get(7)); - addPositions(maxRow.get(5), pafPositions2, Math.max(0, start1 - distance), start1, 1); // calculate coverage... - } - String o2 = maxRow2.get(4); - if ("+".equals(o2)) { - long start2 = Long.parseLong(maxRow2.get(7)); - addPositions(maxRow2.get(5), pafPositions2, Math.max(0, start2 - distance), start2, 1); // calculate coverage... - } else { - long end2 = Long.parseLong(maxRow2.get(8)); - long length2 = Long.parseLong(maxRow2.get(6)); - addPositions(maxRow2.get(5), pafPositions2, end2 + 1, Math.min(length2 + 1, end2 + 1 + distance), 1); // calculate coverage... - } - - } - } - - HashMap> chainPositions = new HashMap>(); - public void processErrorsFromChain(String fn) - { - System.err.println("loading chain..."); - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - do { - ArrayList row = Input.loadTableRow(br, "\t "); - if (row == null) - break; - if (row.get(4).equals("+")) { - String contig1 = row.get(2); - long start1 = Long.parseLong(row.get(5)) + 1; - long end1 = Long.parseLong(row.get(6)); - long aScore = Long.parseLong(row.get(1)); - - long length1 = Long.parseLong(row.get(3)); - - addContig(contig1, length1); - addPositions(contig1, chainPositions, start1, end1, aScore / length1); - - String contig2 = row.get(7); - long length2 = Long.parseLong(row.get(8)); - addContig(contig2, length2); - long start2 = Long.parseLong(row.get(10)); - long end2 = Long.parseLong(row.get(11)); - if (row.get(9).equals("+")) - addPositions(contig2, chainPositions, start2 + 1, end2, aScore / length2); - else { - addPositions(contig2, chainPositions, length2 - end2 + 1, length2 - start2, aScore / length2); - } - } - } while (true); - - } - catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - } - } - -// 1 (n starts) -// 2,3 (n/2) -// 4,5,6,7 (n/4) -// 8,9,10,11,12,13,14,15 (n/8) -// 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 (n/16) -// ... - - private int getPosition(Marker m, boolean plusOrientation) { - if (plusOrientation) - return m.pPlus; - else - return m.pMinus; - } - - HashMap> proximityPositions = new HashMap>(); - HashMap> proximityPositions2 = new HashMap>(); - public void processErrorsFromProximity(String fn, int bin, int maxD, double scale) - { - System.err.println("loading proximity data..."); - long maxDistance = bin * maxD; - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - do { - ArrayList row = Input.loadTableRow(br, "\t "); - if (row == null) - break; - if (row.size() < 5) { - System.err.println("Warning: skipping " + row); - continue; - } - - String contig1 = row.get(0); - String contig2 = row.get(2); - long pos1 = Long.parseLong(row.get(1)); - long pos2 = Long.parseLong(row.get(3)); - long weight = (long) Double.parseDouble(row.get(4)); - if (contig1.equals(contig2)) { - if (pos1 < pos2 && pos2 - pos1 < maxDistance) { - addPositions(contig1, proximityPositions, pos1, pos2, weight); - } - } else { - addPositions(contig1, proximityPositions2, pos1, pos1 + bin, weight); - addPositions(contig2, proximityPositions2, pos2, pos2 + bin, weight); - } - } while (true); - - for (String key : proximityPositions.keySet()) - if (!contigHash.containsKey(key)) - addContig(key, 0); - - for (String key : proximityPositions2.keySet()) - if (!contigHash.containsKey(key)) - addContig(key, 0); - - - for (String contig : contigs) { - ArrayList list = proximityPositions.get(contig); - ArrayList cov = null; - if (list != null) - cov = Misc.cov(list); - else { - cov = new ArrayList(); - cov.add(0L); - cov.add(0L); - } - - ArrayList list2 = proximityPositions2.get(contig); - ArrayList cov2 = null; - if (list2 != null) - cov2 = Misc.cov(list2); - else { - cov2 = new ArrayList(); - cov2.add(0L); - cov2.add(0L); - } - - mergeCovs(contig, cov, cov2); - //for (int i = 0; i < cov.size(); i+=2) { - // System.out.println(cov.get(i) + "\t" + cov.get(i + 1)); - //} - } - - } - catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - } - - - } - - public void findErrors() { - int numContigs = contigs.size(); - for (int ci = 0; ci < numContigs; ++ci) { - String contig = contigs.get(ci); - long length = contigLengths.get(ci); - ArrayList list = pafPositions.get(contig); - } - - /*ArrayList t = new ArrayList(); - t.add(-2l); - t.add(-2l); - t.add(-1l); - t.add(-1l); - t.add(-1l); - t.add(10l); - t.add(10l); - t.add(10l); - t.add(11l); - t.add(12l); - cov(t);*/ - //paf - //for (String contig: contigs) { - // ArrayList list = pafCoverage.get(contig); - // if (list != null) { - // Collections.sort(list); - //System.err.println(list); - //cov(list); - //ArrayList new_list = new ArrayList(); - // } - //} - //chain - - //prox - - //map - - } - /* - if (processErrors) { - for (int c1 = 0; c1 < numContigs; ++c1) { - String contig1 = contigs.get(c1); - ArrayList list = pafCoverage.get(contig1); - if (list == null) { - list = new ArrayList(); - pafCoverage.put(contig1, list); - } - ArrayList> rows1 = alignmentHash.get(contig1); - - for (ArrayList row1 : rows1) { - long start1 = Long.parseLong(row1.get(7)) + 1; - long end1 = Long.parseLong(row1.get(8)); - list.add(-start1); // add -start - list.add(end1); // and end position - } - - //TODO: Try joining adjacent aligment blocks if the gap <= min{maxBridge, length1, length2} - /*ArrayList comp = new ArrayList(); // sort row by start position... - /for (ArrayList row1 : rows1) - comp.add(Long.parseLong(row1.get(7))); // start - Misc.ArrayIndexComparator comparator = new Misc.ArrayIndexComparator(comp); - Integer[] indexes = comparator.createIndexArray(); - Arrays.sort(indexes, comparator); - - ArrayList list = pafCoverage.get(contig1); - if (list == null) { - list = new ArrayList(); - pafCoverage.put(contig1, list); - } - - for (int orientation = 0; orientation < 2; ++orientation) { - long minStart = Long.MAX_VALUE; - long maxEnd = 0; - - long minRStart = Long.MAX_VALUE; - long maxREnd = 0; - for (int i : indexes) { - ArrayList row1 = rows1.get(i); - - if ((orientation != 0) ^ ("+".equals(row1.get(4)))) { // take only + or - orientation... - long start1 = Long.parseLong(row1.get(7)) + 1; - long end1 = Long.parseLong(row1.get(8)); - - long oldStart = minStart; - long oldEnd = maxEnd; - - minStart = Math.min(minStart, start1); - maxEnd = Math.max(maxEnd, end1); - - long rstart1 = Long.parseLong(row1.get(2)); - long rend1 = Long.parseLong(row1.get(3)); - - minRStart = Math.min(minRStart, rstart1); - maxREnd = Math.max(maxREnd, rend1); - - long RL = maxREnd - minRStart; // read length - long L = maxEnd - minStart; // contig length - - if (oldEnd == 0 || RL <= 1.5 * L && L <= 1.5 * RL) { - - } else { - list.add(-oldStart); // add -start - list.add(oldEnd); // and end position - } - - } - } - }*/ - - - private static void usageInfo() - { - System.err.println("usage: java FindContigErrors [options]"); - System.err.println(" chain=file chain file "); - - System.err.println(" paf=file load alignment file in paf (minimap2) format"); - - System.err.println(" proximity=file NUM1 NUM2 NUM3 load proximity data, NUM1=bin size [10000]"); - System.err.println(" NUM2=max distance in bins[25], NUM3=scale score [1.0]"); - } - - private static void test() - { - ArrayList list = new ArrayList(); - list.add(new long[]{0, 9, 1}); - list.add(new long[]{0, 10, 1}); - list.add(new long[]{0, 11, 1}); - list.add(new long[]{5, 10, 1}); - FindContigErrors fce = new FindContigErrors(); - System.err.println(Misc.cov(list)); - } - - public static void main(String[] args) - { - //FindContigErrors.test(); - - if (args.length == 0) { - usageInfo(); - System.exit(0); - } - String extraParameters = ""; - for (int i = 0; i < args.length; ++i) { - extraParameters += " " + args[i]; - } - ParameterParser pp = new ParameterParser(); - if (!pp.init(extraParameters)) { - usageInfo(); - System.exit(0); - } - pp.warning(new String[]{"paf", "chain", "proximity"}); - - FindContigErrors fe = new FindContigErrors(); - System.out.println("#java FindContigErrors" + extraParameters); - - //for (int i = 0; i < pp.getNumberOfValues("map"); ++i) { - // fe.processErrorsFromMap(pp.getValueAsString("map", i, null), pp.getValueAsString("noIntervals", "0").equals("1")); - //} - - String chain = pp.getValueAsString("chain", null); - if (chain != null) { - fe.processErrorsFromChain(chain); - } - - String paf = pp.getValueAsString("paf", null); - if (paf != null) - fe.processErrorsFromPaf(paf); - - String prox = pp.getValueAsString("proximity", 0, null); - if (prox != null) { - int bin = Integer.parseInt(pp.getValueAsString("proximity", 1, "10000")); - int maxD = Integer.parseInt(pp.getValueAsString("proximity", 2, "25")); - double scale = Double.parseDouble(pp.getValueAsString("proximity", 3, "1.0")); - fe.processErrorsFromProximity(prox, bin, maxD, scale); - } - fe.findErrors(); - } - -} diff --git a/software/LepAnchor/src/Input.java b/software/LepAnchor/src/Input.java deleted file mode 100644 index 0d57214..0000000 --- a/software/LepAnchor/src/Input.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ -/** - This file is part of Lep-MAP. - - Lep-MAP is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-MAP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-MAP. If not, see . - - Copyright (C) 2013 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki -*/ - -//Reads files to ArrayLists of Strings -import java.io.*; -import java.util.*; - -public class Input { - private Input() { }; - - private static boolean keepComments = false; - private static StringBuilder comments = new StringBuilder(); - public static String getComments() - { - return comments.toString(); - } - public static void setKeepComments(boolean value) - { - keepComments = value; - } - - public static String loadRow(BufferedReader br) throws Exception - { - String s = ""; - do { - s = br.readLine(); - //System.err.println(s); - if (s != null) { - int index = s.indexOf('#'); - if (index >= 0) { - s = s.substring(0, index); - if (keepComments) { - comments.append(s.substring(index + 1)); - comments.append('\n'); - } - } - } - } while ( s != null && (s.length() == 0) ); - return s; - } - - public static ArrayList splitRow(String s, String delim) - { - StringTokenizer st = new StringTokenizer(s, delim, false); - ArrayList row = new ArrayList(); - while (st.hasMoreTokens()) { - String nt = st.nextToken(); - row.add(nt); - } - return row; - } - - - public static ArrayList loadTableRow(BufferedReader br, String delim) throws Exception - { - String s = loadRow(br); - if (s == null) - return null; - - /*String sa[] = s.split(delim); - ArrayList row = new ArrayList(); - for (String tmp : sa) - row.add(tmp);*/ - return splitRow(s, delim); - } - - public static ArrayList> loadTable(BufferedReader br, String delim) throws Exception { - ArrayList> ret = new ArrayList>(); - //ArrayList comments = new ArrayList(); - while (true) { - ArrayList row = loadTableRow(br, delim); - if (row == null) - break; - else - ret.add(row); - } - return ret; - } - - public static ArrayList> loadTable(BufferedReader br, String delim, String returnDelim) throws Exception { - ArrayList> ret = new ArrayList>(); - - //ArrayList comments = new ArrayList(); - while (true) { - StringTokenizer st = null; - String s = null; - do { - s = loadRow(br); - if (s != null) - st = new StringTokenizer(s, delim, true); - } while ( s != null && !st.hasMoreTokens()); - if (s == null) - break; - ArrayList row = new ArrayList(); - while (st.hasMoreTokens()) { - String nt = st.nextToken(); - if (delim.indexOf(nt) >= 0) { - if (returnDelim.indexOf(nt) >= 0) - row.add(nt); - } else - row.add(nt); - } - ret.add(row); - } - return ret; - } - - public static ArrayList> loadTable(String filename, String delim, String returnDelim) { - try { - BufferedReader br = new BufferedReader(new FileReader(filename)); - ArrayList> t = loadTable(br, delim, returnDelim); - br.close(); - return t; - - } catch (Exception e) { - System.err.println(e); - return null; - } - } - - - public static ArrayList> loadTable(String filename, String delim) { - try { - BufferedReader br = new BufferedReader(new FileReader(filename)); - ArrayList> t = loadTable(br, delim); - br.close(); - return t; - - } catch (Exception e) { - System.err.println(e); - return null; - } - } - - public static int[][] loadIntTable(String filename, String delim) { - - ArrayList ret = new ArrayList(); - try { - BufferedReader br = new BufferedReader(new FileReader(filename)); - while (true) { - ArrayList t = loadTableRow(br, delim); - if (t == null) - break; - else { - int it[] = new int[t.size()]; - int i = 0; - for (String s : t) - it[i++] = Integer.parseInt(s); - ret.add(it); - } - - } - br.close(); - return ret.toArray(new int[ret.size()][]); - - } catch (Exception e) { - System.err.print(e); - return null; - } - } - - public static void main(String args[]) - { - ArrayList> test = Input.loadTable("pedigree.txt", " \t"); - for (ArrayList row : test) { - for (String item : row) - System.err.print(item + " "); - System.err.println(); - } - - - } - -} diff --git a/software/LepAnchor/src/InputData.java b/software/LepAnchor/src/InputData.java deleted file mode 100644 index 0e257fc..0000000 --- a/software/LepAnchor/src/InputData.java +++ /dev/null @@ -1,452 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; - - - -public class InputData { - - public static long myParseLong(String s) - { - if (s.charAt(s.length() - 1) == '*') - return Long.parseLong(s.substring(0, s.length() - 1)); - else - return Long.parseLong(s); - - } - private static long[] myParseLongInterval(String s) - { - long ret[] = null; - int pos = s.indexOf('-'); - if (pos < 0) { - long p = myParseLong(s); - return new long[]{p, p}; - } - if (s.charAt(s.length() - 1) == '*') { - ret = new long[3]; - ret[1] = Long.parseLong(s.substring(pos + 1, s.length() - 1)); - } - else { - ret = new long[2]; - ret[1] = Long.parseLong(s.substring(pos + 1)); - } - ret[0] = Long.parseLong(s.substring(0, pos)); - return ret; - } - - public static HashMap>> loadRaw(String fn, HashMap> haplotypeHash) - { - HashMap>> ret = new HashMap>>(); - try { - - int numMarkers = 0; - - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - do { - String row = Input.loadRow(br); - if (row == null) - break; - ArrayList srow = Input.splitRow(row, "[\t ]"); - if (srow.size() >= 3) { - String contig = srow.get(0); - boolean added = false; - if (haplotypeHash != null && haplotypeHash.containsKey(contig)) { - long pos = myParseLong(srow.get(1)); - for (ContigInterval ci : haplotypeHash.get(contig)) { - if (ci.inside(pos)) { - ArrayList> list = ret.get(ci); - if (list == null) { - list = new ArrayList>(); - ret.put(ci, list); - } - list.add(srow); - ++numMarkers; - added = true; - break; - } - } - } - if (!added) - System.out.println(row); - } else - System.err.println("Warning: skipping " + row); - } while (true); - - System.err.println("Liftover for " + numMarkers + " markers over " + ret.size() + " regions"); - return ret; - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - return null; - } - } - - public static ArrayList loadMap(String fn, boolean nochromosome, boolean nointervals, boolean compressMap) - { - ArrayList pos = new ArrayList(); - ArrayList chr = new ArrayList(); - - ArrayList ret = new ArrayList(); - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - do { - ArrayList row = Input.loadTableRow(br, "[\t ]"); - if (row == null) - break; - if (row.size() > 0) { - if (nochromosome) - row.add(2, "1"); - - if (row.size() >= 4 && (nointervals || (row.size() & 1) == 1) || row.size() == 4) { - int intervals[] = new int[Math.max(2, row.size() - 3)]; - if (row.size() == 4 || nointervals) { - if (nointervals) { - intervals = new int[2]; - double p = Double.parseDouble(row.get(3)); - if (row.size() >= 5) - p += Double.parseDouble(row.get(4)); - pos.add(p); - chr.add(Integer.parseInt(row.get(2))); - } - else - try { - intervals[0] = intervals[1] = Integer.parseInt(row.get(3)); - } catch (NumberFormatException e) { - System.err.println("Error: non-integer value for interval (check parameter noIntervals=1)" + row); - System.exit(-1); - } - } - else - for (int i = 3; i < row.size(); i+=2) { - try { - intervals[i-3] = Integer.parseInt(row.get(i)); - intervals[i-2] = Integer.parseInt(row.get(i + 1)); - } catch (NumberFormatException e) { - System.err.println("Error: non-integer value for interval (check parameter noIntervals=1) " + row); - System.exit(-1); - } - } - ret.add(new Marker(row.get(0), myParseLong(row.get(1)), Integer.parseInt(row.get(2)), intervals)); - } else - System.err.println("Warning: skipping " + row); - } - } while (true); - br.close(); - if (nointervals) { - //put markers in each chr to separate list... and sort... - HashMap> chrMarkers = new HashMap>(); // store chromosome info - HashMap> indexMarkers = new HashMap>(); // store original index in ret... - - for (int mi = 0; mi < chr.size(); ++mi) { - int c = chr.get(mi); - ArrayList list = chrMarkers.get(c); - ArrayList list2 = indexMarkers.get(c); - if (list == null) { - list = new ArrayList(); - chrMarkers.put(c, list); - list2 = new ArrayList(); - indexMarkers.put(c, list2); - } - list.add(pos.get(mi)); - list2.add(mi); - } - for (int c : chrMarkers.keySet()) { - ArrayList list = chrMarkers.get(c); - ArrayList list2 = indexMarkers.get(c); - Misc.ArrayIndexComparator comparator = new Misc.ArrayIndexComparator(list); - Integer[] indexes = comparator.createIndexArray(); - java.util.Arrays.sort(indexes, comparator); - - double prev = Double.NEGATIVE_INFINITY; - int ip = -1; - for (int i : indexes) { - double p = list.get(i); - if (p > prev) - ++ip; - Marker m = ret.get(list2.get(i)); - m.intervals[0] = ip; - m.intervals[1] = ip; - prev = p; - } - - } - } else if (compressMap){ // no noIntervals && compress map positions - - // compressing map speeds up computation... - HashMap> chrMarkers = new HashMap>(); - - for (Marker m : ret) { - int c = m.getChromosome(); - ArrayList list = chrMarkers.get(c); - if (list == null) { - list = new ArrayList(); - chrMarkers.put(c, list); - } - list.add(m); - } - for (ArrayList markers: chrMarkers.values()) { - ArrayList positions = new ArrayList(); - for (Marker m : markers) { - int[] interval = m.getIntervals(); - for (int i : interval) - positions.add(i); - } - Collections.sort(positions); - HashMap compressHash = new HashMap(); - int numPositions = 0; - for (int i : positions) { - if (!compressHash.containsKey(i)) - compressHash.put(i, numPositions++); - } - for (Marker m : markers) { - int[] interval = m.getIntervals(); - for (int ii = 0; ii < interval.length; ++ii) - interval[ii] = compressHash.get(interval[ii]); - } - } - } - return ret; - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - return null; - } - } - public static ArrayList loadBedAndComments(String fn, StringBuilder comments) - { - Input.setKeepComments(true); - ArrayList ret = loadBed(fn, -1); - Input.setKeepComments(false); - comments.append(Input.getComments()); - return ret; - } - - public static ArrayList loadBed(String fn) - { - return loadBed(fn, -1); - } - public static ArrayList loadBed(String fn, int chr) - { - ArrayList ret = new ArrayList(); - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - do { - ArrayList row = Input.loadTableRow(br, "[\t ]"); - if (row == null) - break; - if (row.size() >= 3) { - String start = row.get(1); - String end = row.get(2); - ContigInterval ci = null; - if (start.indexOf('-') >= 0 || end.indexOf('-') >= 0) - ci = new ContigInterval(row.get(0), myParseLongInterval(start), myParseLongInterval(end)); - else - ci = new ContigInterval(row.get(0), myParseLong(start), myParseLong(end)); - if (row.size() >= 4 && (row.get(3).equals("-") || row.get(3).equals("--") || row.get(3).equals("---"))) - ci.flipOrientation(); - - if (row.size() >= 5) { - int bChr = Integer.parseInt(row.get(4)); - ci.setChromosome(bChr); - } - if (chr < 0 || ci.getChromosome() == chr) - ret.add(ci); - } - else - System.err.println("Warning: skipping " + row); - } while (true); - - int prevC = 0; - for (ContigInterval ci : ret) { - if (prevC != 0 && ci.getChromosome() != prevC) { - System.err.println("Error: multiple cromosomes in the bed"); - System.err.println("Try providing parameter chromosome or split bed into chromosomes"); - System.exit(-1); - } - } - br.close(); - return ret; - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - return null; - } - } - - public static ArrayList loadLa(String fn) - { - ArrayList ret = new ArrayList(); - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - do { - ArrayList row = Input.loadTableRow(br, "[\t ]"); - if (row == null) - break; - if (row.size() >= 3) { - String start = row.get(1); - String end = row.get(2); - if (row.size() >= 8) { - start = row.get(6); - int sis = start.indexOf('/'); - if (sis >= 0) - start = start.substring(0, sis); - end = row.get(7); - int sie = end.indexOf('/'); - if (sie >= 0) - end = end.substring(0, sie); - } - ContigInterval ci = null; - if (start.indexOf('-') >= 0 || end.indexOf('-') >= 0) - ci = new ContigInterval(row.get(0), myParseLongInterval(start), myParseLongInterval(end)); - else - ci = new ContigInterval(row.get(0), myParseLong(start), myParseLong(end)); - if (row.size() >= 4 && (row.get(3).equals("-") || row.get(3).equals("--") || row.get(3).equals("---"))) - ci.flipOrientation(); - - if (row.size() >= 5) { - int bChr = Integer.parseInt(row.get(4)); - ci.setChromosome(bChr); - } - ret.add(ci); - } - else - System.err.println("Warning: skipping " + row); - } while (true); - - int prevC = 0; - for (ContigInterval ci : ret) { - if (prevC != 0 && ci.getChromosome() != prevC) { - System.err.println("Error: multiple cromosomes in the bed"); - System.err.println("Try providing parameter chromosome or split bed into chromosomes"); - System.exit(-1); - } - } - br.close(); - return ret; - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - return null; - } - } - - - public static ArrayList loadHaplotypes(String fn) - { - ArrayList ret = new ArrayList(); - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - do { - ArrayList row = Input.loadTableRow(br, "[\t ]"); - if (row == null) - break; - if (row.size() >= 4) { - ContigInterval ci = new ContigInterval(row.get(1), myParseLong(row.get(2)), myParseLong(row.get(3))); - ret.add(ci); - } - else - System.err.println("Warning: skipping haplotype " + row); - } while (true); - br.close(); - return ret; - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - return null; - } - } - - - public ArrayList loadLGMap(String fn) - { - ArrayList ret = new ArrayList(); - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - String line = null; - do { - line = br.readLine(); - if (line == null) - break; - - int ic = line.indexOf('#'); - if (ic >= 0) - line = line.substring(0, ic); - - if (line.length() > 0) { - String[] row = line.split("\t"); - if (row.length > 2 || row.length == 3 || (row.length & 1) == 0) { - int intervals[] = new int[Math.max(2, row.length - 2)]; - if (row.length == 3) - intervals[0] = intervals[1] = Integer.parseInt(row[2]); - else - for (int i = 2; i < row.length; i+=2) { - intervals[i-2] = Integer.parseInt(row[i]); - intervals[i-1] = Integer.parseInt(row[i + 1]); - } - //ret.add(new Marker(row[0], myParseInt(row[1]), intervals)); - } else - System.err.println("Warning: skipping " + line); - } - } while (true); - br.close(); - return ret; - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - return null; - } - } - - -} diff --git a/software/LepAnchor/src/LiftoverHaplotypes.java b/software/LepAnchor/src/LiftoverHaplotypes.java deleted file mode 100644 index 7edd03c..0000000 --- a/software/LepAnchor/src/LiftoverHaplotypes.java +++ /dev/null @@ -1,44 +0,0 @@ - -public class LiftoverHaplotypes { - private static void usageInfo() - { - System.err.println("usage: java LiftoverHaplotypes haplotypes=haplo.bed map=mapX.txt chain=all.chain >mapX_liftover.txt"); - System.err.println(" haplotypes=FILE try liftover for markers in removed haplotypes listed in FILE (from findFullHaplotypes or from PlaceAndOrientContigs)"); - System.err.println(" map=FILE2 any file with contig and pos in the first columns, typically the map file"); - System.err.println(" chain=FILE3 the chain file"); - } - - public static void main(String[] args) - { - - if (args.length == 0) { - usageInfo(); - System.exit(0); - } - String extraParameters = ""; - for (int i = 0; i < args.length; ++i) { - extraParameters += " " + args[i]; - } - ParameterParser pp = new ParameterParser(); - if (!pp.init(extraParameters)) { - usageInfo(); - System.exit(0); - } - pp.warning(new String[]{"map", "chain", "haplotypes"}); - - String haplotypes = pp.getValueAsString("haplotypes", null); - String chain = pp.getValueAsString("chain", null); - String map = pp.getValueAsString("map", null); - if (map == null || chain == null || haplotypes == null){ - usageInfo(); - System.exit(0); - } - - - System.out.println("#java LiftoverHaplotypes" + extraParameters); - - PlaceAndOrientContigs poc = new PlaceAndOrientContigs(); - poc.liftover(haplotypes, chain, map); - } - -} diff --git a/software/LepAnchor/src/Map2Bed.java b/software/LepAnchor/src/Map2Bed.java deleted file mode 100644 index 6759df9..0000000 --- a/software/LepAnchor/src/Map2Bed.java +++ /dev/null @@ -1,334 +0,0 @@ -import java.io.BufferedReader; -import java.io.FileReader; -import java.util.ArrayList; -import java.util.HashMap; - -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ -public class Map2Bed { - private int markerSupport = 2; - private int splitLength = 10000; - private double minQuality = 2; - - public static void usage() - { - System.err.println("Usage: java Map2Bed map=contig_pos_map.txt contigLengths=contig.sizes [options] >map_clean.bed"); - System.err.println(" map=file a map file containing columns contig, position and chromosome "); - System.err.println(" in sorted order (typically from CleanMap)"); - - System.err.println(" contigLength=file a file containing columns contig name and its length"); - - System.err.println(" markerSupport=NUM at least NUM markers are needed for splitting a contig [2]"); - System.err.println(" minSplitLength=NUM do not split shorter regions than NUM [10000]"); - System.err.println(" minQuality=NUM only consider markers with quality >= NUM [2]"); - System.err.println(" (column 5 from CleanMap)"); - } - private void makeBed(String mapFile, String lengthFile) - { - - HashMap scaffoldLength = new HashMap(); - try { - BufferedReader br = new BufferedReader(new FileReader(lengthFile)); - - ArrayList line = Input.loadTableRow(br, " \t"); - while (line != null) { - String scaffold = line.get(0); - long length = Long.parseLong(line.get(1)); - scaffoldLength.put(scaffold, length); - line = Input.loadTableRow(br, " \t"); - } - } catch (Exception e) { - System.err.println("Error loading contig lengths"); - e.printStackTrace(); - System.exit(-1); - } - - int numChromosomes = 0; - String prevScaffold = ""; - ArrayList list = new ArrayList(); - - HashMap scaffoldMap = new HashMap(); - try { - BufferedReader br = new BufferedReader(new FileReader(mapFile)); - - ArrayList line = Input.loadTableRow(br, " \t"); - while (line != null) { - if (!line.get(0).equals("CHR") && !line.get(0).equals("CHROM")) { - String scaffold = line.get(0); - long position = Long.parseLong(line.get(1)); - int chr = Integer.parseInt(line.get(2)); - double quality = minQuality; - if (line.size() >= 5) - quality = Double.parseDouble(line.get(4)); - if (chr > 0 && quality >= minQuality) { - if (scaffoldMap.containsKey(scaffold) && scaffoldMap.get(scaffold) > position) { - System.err.println("Error: map file must be sorted"); - System.exit(-1); - } - if (!scaffoldLength.containsKey(scaffold)) { - System.err.println("Error: contig " + scaffold + " does not have length in " + lengthFile); - System.exit(-1); - } - - if (!prevScaffold.equals("") && !prevScaffold.equals(scaffold)) { - processScaffold(prevScaffold, scaffoldLength.get(prevScaffold), list); - list.clear(); - } - scaffoldMap.put(scaffold, position); - prevScaffold = scaffold; - - list.add(position); - list.add((long)chr); - list.add((long)(100 * quality)); - numChromosomes = Math.max(numChromosomes, chr); - } - } - line = Input.loadTableRow(br, " \t"); - } - processScaffold(prevScaffold, scaffoldLength.get(prevScaffold), list); - } catch (Exception e) { - System.err.println("Error loading map file"); - e.printStackTrace(); - System.exit(-1); - } - } - private class Interval{ - int startIndex; - int endIndex; - long quality; - long position; - long startPosition; - long chr; - int markers; - public Interval(int startIndex, long position, long quality, long chr){ - this.startIndex = startIndex; - this.endIndex = startIndex; - this.quality = quality; - this.chr = chr; - this.position = position; - startPosition = position; - markers = 1; - } - public void addMarker(int index, long position, long quality) { - endIndex = index; - this.quality += quality; - this.position = position; - ++markers; - } - - public void addMarkerLeft(int index, long position, long quality) { - startIndex = index; - this.quality += quality; - this.startPosition = position; - ++markers; - } - - public void addInterval(Interval i) { - endIndex = i.endIndex; - quality += i.quality; - position = i.position; - markers += i.markers; - } - public int getMarkers() { - return markers; - } - public long getLength() { - return position - startPosition + 1; - } - public long getQuality() { - return quality; - } - public long getChr() { - return chr; - } - public int getStartIndex() { - return startIndex; - } - public int getEndIndex() { - return endIndex; - } - public long getStartPosition() { - return startPosition; - } - - public long getPosition() { - return position; - } - public String toString() - { - //return chr + "\t" + markers + "\t" + quality; - return startPosition + "\t" + position + "\t" + chr; - } - } - - private void processScaffold(String scaffold, long scaffoldLength, ArrayList list) - { - - //create intervals of adjacent markers in the same chromosome - ArrayList intervals = new ArrayList(); - long prevChr = 0; - for (int i = 0; i < list.size(); i+=3) { - long position = list.get(i); - long chr = list.get(i + 1); - long quality = list.get(i + 2); - if (i == 0 || prevChr != chr) - intervals.add(new Interval(i, position, quality, chr)); - else - intervals.get(intervals.size() - 1).addMarker(i, position, quality); - prevChr = chr; - } - - if (intervals.size() > 1) { //more than one interval, prune - do { - int removed = -1; - for (int ii = 0; ii < intervals.size(); ++ii) { - Interval i = intervals.get(ii); - if (i.getMarkers() < markerSupport || i.getLength() < splitLength) { // < markerSupport markers and/or length < splitLength - if (removed < 0) - removed = ii; - else { - long l = i.getLength(); - long lr = intervals.get(removed).getLength(); - if (l < lr || (l == lr && i.getQuality() < intervals.get(removed).getQuality())) - removed = ii; - } - } - } - - if (removed >= 0) { //remove removed (too short and/or too few markers) - if (removed > 0 && removed + 1 < intervals.size() && intervals.get(removed - 1).getChr() == intervals.get(removed + 1).getChr()) { - Interval prev = intervals.get(removed - 1); - Interval next = intervals.get(removed + 1); - for (int li = prev.getEndIndex() + 3; li < next.getStartIndex(); li+=3) { - long chr = list.get(li + 1); - if (chr == prev.getChr()) { - long position = list.get(li); - long quality = list.get(li + 2); - prev.addMarker(li, position, quality); - } - } - prev.addInterval(next); - intervals.remove(removed + 1); - intervals.remove(removed); - } else - intervals.remove(removed); - } - else - break; - } while (true); - - //try to extend intervals - for (int ii = 0; ii + 1 < intervals.size(); ++ii) { - Interval prev = intervals.get(ii); - Interval next = intervals.get(ii + 1); - - for (int li = prev.getEndIndex() + 3; li < next.getStartIndex(); li+=3) { - long chr = list.get(li + 1); - if (chr == next.getChr()) // conflict - break; - if (chr == prev.getChr()) { - //System.err.println("Extend"); - long position = list.get(li); - long quality = list.get(li + 2); - prev.addMarker(li, position, quality); - } - } - - for (int li = next.getStartIndex() - 3; li > prev.getEndIndex(); li-=3) { - long chr = list.get(li + 1); - if (chr == prev.getChr()) // conflict - break; - if (chr == next.getChr()) { - //System.err.println("Extend"); - long position = list.get(li); - long quality = list.get(li + 2); - next.addMarkerLeft(li, position, quality); - } - } - } - - } - - for (int ii = 0; ii < intervals.size(); ++ii) { - Interval i = intervals.get(ii); - long start = 1; - if (ii > 0) - start = intervals.get(ii - 1).getPosition() + 1; - - String end = scaffoldLength + "*"; - if (ii + 1 < intervals.size()) - end = "" + (intervals.get(ii + 1).getStartPosition() - 1); - System.out.println(scaffold + "\t" + start + "-" + i.getStartPosition() + "\t" + i.getPosition() + "-" + end + "\t?\t" + i.getChr()); - } - - //System.err.println(scaffold + "\t" + intervals); - } - - private void setMarkerSupport(int parseInt) { - markerSupport = parseInt; - } - private void setSplitLength(int parseInt) { - splitLength = parseInt; - } - private void setMinQuality(double parseDouble) { - minQuality = parseDouble; - } - - public static void main(String args[]) - { - ParameterParser pp = new ParameterParser(); - - String extraParameters = ""; - for (int i = 0; i < args.length; ++i) { - extraParameters += " " + args[i]; - } - if (args.length == 0 || !pp.init(extraParameters)) { - usage(); - System.exit(0); - } - - - String mapFile = pp.getValueAsString("map", null); - String lengthFile = pp.getValueAsString("contigLength", null); - if (mapFile == null) { - System.err.println("Please specify a map file"); - usage(); - System.exit(0); - - } - if (lengthFile == null) { - System.err.println("Please specify a contig length file"); - usage(); - System.exit(0); - - } - - pp.warning(new String[]{"map", "contigLength", "markerSupport", "minSplitLength", "minQuality"}); - - - Map2Bed m2p = new Map2Bed(); - m2p.setMarkerSupport(Integer.parseInt(pp.getValueAsString("markerSupport", "2"))); - m2p.setSplitLength(Integer.parseInt(pp.getValueAsString("minSplitLength", "10000"))); - m2p.setMinQuality(Double.parseDouble(pp.getValueAsString("minQuality", "2.0"))); - - System.out.println("#java Map2Bed" + extraParameters); - m2p.makeBed(mapFile, lengthFile); - } -} diff --git a/software/LepAnchor/src/Marker.java b/software/LepAnchor/src/Marker.java deleted file mode 100644 index 181da59..0000000 --- a/software/LepAnchor/src/Marker.java +++ /dev/null @@ -1,139 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ -public class Marker implements Comparable{ - String contig; - int chromosome; - long position; - int intervals[]; - ContigInterval ci; - - int pPlus; - int pMinus; - - - public Marker(String contig, long position, int chromosome, int intervals[]) - { - this.contig = contig; - this.position = position; - this.chromosome = chromosome; - //TODO: consider cloning intervals... - this.intervals = intervals; - } - - public Marker(Marker m, long position) - { - this(m.contig, position, m.chromosome, m.intervals); - } - - public Marker(Marker m) - { - this(m.contig, m.position, m.chromosome, m.intervals); - } - - public Marker(Marker m, ContigInterval ci) - { - this(m.contig, m.position, m.chromosome, m.intervals); - this.ci = ci; - } - - //return next position inside from p or p + 1 if p is inside - public int nextInside(int p) { - int min = Integer.MAX_VALUE; - for (int j = 0; j < intervals.length; j+=2) - if (p <= intervals[j + 1] && min > intervals[j]) - min = intervals[j]; - if (min <= p) - return p + 1; - return min; - } - - public int inside(int p) - { - for (int j = 0; j < intervals.length; j+=2) - if (p >= intervals[j] && p <= intervals[j + 1]) - return 1; - return 0; - } - @Override - public String toString() - { - return contig + "\t" + position + "\t" + chromosome; - } - //orders first by contig and then by position... - @Override - public int compareTo(Marker o) { - int ret = getContig().compareTo(o.getContig()); - if (ret != 0) - return ret; - if (position < o.position) - return -1; - if (position > o.position) - return 1; - return 0; - } - public String getName(){ - return contig + '\t' + position; - } - public String getContig(){ - return contig; - } - public long getPosition(){ - return position; - } - - public int getChromosome(){ - return chromosome; - } - public int[] getIntervals() { - return intervals; - } - - public int maxBin() // get maximum bin value... - { - int ret = 0; - for (int j = 1; j < intervals.length; j+=2) - ret = Math.max(ret, intervals[j]); - return ret; - } - public int minBin() // get minimum bin value... - { - int ret = Integer.MAX_VALUE; - for (int j = 0; j < intervals.length; j+=2) - ret = Math.min(ret, intervals[j]); - return ret; - } - public void flip(int maxBin) - { - int i[] = new int[intervals.length]; - for (int j = 0; j < intervals.length; ++j) - i[j] = maxBin - intervals[j ^ 1]; - intervals = i; - } - - public ContigInterval getContigInterval() { - return ci; - } - public static void main(String args[]){ - Marker m = new Marker("Contig", 1, 1, new int[]{10,20,40,60}); - for (int i = 0; i <= 61; ++i) - System.out.println(i + ":" + m.nextInside(i)); - } - -} diff --git a/software/LepAnchor/src/Misc.java b/software/LepAnchor/src/Misc.java deleted file mode 100644 index 2c7ffe0..0000000 --- a/software/LepAnchor/src/Misc.java +++ /dev/null @@ -1,224 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ -/** - This file is part of Lep-MAP. - - Lep-MAP is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-MAP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-MAP. If not, see . - - Copyright (C) 2013 Pasi Rastas, pasi.rastas@helsinki.fi, University of Helsinki -*/ - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; - -public class Misc { - - //compare based on first element of long[] - private static class LongComparator0 implements Comparator - { - @Override - public int compare(long[] l1, long[] l2){ - return Long.compare(l1[0], l2[0]); - } - } - - //compare based on second element of long[] - private static class LongComparator1 implements Comparator - { - @Override - public int compare(long[] l1, long[] l2){ - return Long.compare(l1[1], l2[1]); - } - } - //list elements contains 3 values, start, end and weight as long[3] - //dynamic programming for finding coverage of multiple weighted intervals - public static ArrayList cov(ArrayList list_) - { - ArrayList listStop = new ArrayList(); - listStop.addAll(list_); - ArrayList list = new ArrayList(); - list.addAll(list_); - Collections.sort(list, new LongComparator0()); - Collections.sort(listStop, new LongComparator1()); - - ArrayList ret = new ArrayList(); - int n = list.size(); - int end = 0; - int start = 0; - long pos = 0; - - long cov = 0; - long ps = list.get(start)[0]; // start - long pe = listStop.get(end)[1]; // end - - long prevP = 0; - long prevC = 0; - while (end < n) { - if (start < n && ps <= pe) { - pos = ps; - while (start < n && pos == list.get(start)[0]) { // take all intervals with same start - cov += list.get(start)[2]; - ++start; - if (start < n) - ps = list.get(start)[0]; - } - } else { - pos = pe; - while (end < n && pos == listStop.get(end)[1]) { // teka all intervals with same end - cov -= listStop.get(end)[2]; - ++end; - if (end < n) - pe = listStop.get(end)[1]; - } - ++pos; - } - if (pos != prevP) { - //System.err.println(prevP + "\t" + prevC); - ret.add(prevP); - ret.add(prevC); - } - prevP = pos; - prevC = cov; - } - ret.add(pos); - ret.add(0L); - //System.err.println(pos + "\t0"); - return ret; - } - - - public static boolean intersectIntervals(long b1, long e1, long b2, long e2){ - if (e1 < b2 || e2 < b1) - return false; - return true; - } - - public static long intersectIntervalsLength(long b1, long e1, long b2, long e2){ - if (e1 <= b2 || e2 <= b1) - return 0; - return Math.min(e1 - b2, e2 - b1); - } - - private static final double LN10 = Math.log(10.0); - - public static double exp10(double a) - { - return Math.exp(a * LN10); - } - - public static class ArrayIndexComparator> implements Comparator - { - private final T[] array; - - public ArrayIndexComparator(T[] array) - { - this.array = array; - } - @SuppressWarnings("unchecked") - public ArrayIndexComparator(ArrayList alArray) - { - this.array = (T[]) alArray.toArray(new Comparable[0]); - } - - public Integer[] createIndexArray() - { - Integer[] indexes = new Integer[array.length]; - for (int i = 0; i < array.length; i++) - { - indexes[i] = i; - } - return indexes; - } - - public int compare(Integer index1, Integer index2) - { - return array[index1].compareTo(array[index2]); - } - } - //Misc.ArrayIndexComparator comparator = new Misc.ArrayIndexComparator(table); - //Integer[] indexes = comparator.createIndexArray(); - //Arrays.sort(indexes, comparator); - - public static class ArrayIndexComparator2 implements Comparator - { - private final T[] array; - private Comparator C; - - public ArrayIndexComparator2(T[] array, Comparator C) - { - this.array = array; - this.C = C; - } - @SuppressWarnings("unchecked") - public ArrayIndexComparator2(ArrayList alArray, Comparator C) - { - this.array = (T[]) alArray.toArray(new Comparable[0]); - this.C = C; - } - - public Integer[] createIndexArray() - { - Integer[] indexes = new Integer[array.length]; - for (int i = 0; i < array.length; i++) - { - indexes[i] = i; - } - return indexes; - } - - public int compare(Integer index1, Integer index2) - { - return C.compare(array[index1], array[index2]); - } - } - //Misc.ArrayIndexComparator2 comparator = new Misc.ArrayIndexComparator2(table, customDoubleComparator); - //Integer[] indexes = comparator.createIndexArray(); - //Arrays.sort(indexes, comparator); - - //returns random merge of two lists... - public static ArrayList randomMerge(ArrayList l1, ArrayList l2) - { - ArrayList ret = new ArrayList(); - int n = l1.size(); - int m = l2.size(); - int j = 0; - int k = 0; - for (int i = 0; i < n + m; ++i) { - if (j < n && (k == m || Math.random() * (n - j) > (m - k) * 0.5)) - ret.add(l1.get(j++)); - else - ret.add(l2.get(k++)); - } - return ret; - } - -} diff --git a/software/LepAnchor/src/ParameterParser.java b/software/LepAnchor/src/ParameterParser.java deleted file mode 100644 index 555b64f..0000000 --- a/software/LepAnchor/src/ParameterParser.java +++ /dev/null @@ -1,231 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ -/** - This file is part of Lep-MAP. - - Lep-MAP is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-MAP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-MAP. If not, see . - - Copyright (C) 2013 Pasi Rastas, pasi.rastas@helsinki.fi, University of Helsinki -*/ -//Parses parameters given as "x=a y=b c d z = e" -import java.util.StringTokenizer; -import java.util.ArrayList; -import java.util.HashMap; - -public class ParameterParser { - private HashMap> hm; - - public ParameterParser() - { - - } - public ParameterParser(String parameters) - { - if (!init(parameters)) - hm = null; - } - - public boolean init(ArrayList tokens) - { - hm = new HashMap>(); - int n = tokens.size(); - int i = 0; - while (i < n) { - String key = tokens.get(i); - if (key.equals("=")) - return false; - ++i; - if (i < n - 1) { - if (!tokens.get(i).equals("=")) - return false; - ++i; - } else - return false; - - ArrayList value = new ArrayList(); - while (i == n - 1 || (i < n - 1 && !tokens.get(i + 1).equals("="))) { - String nv = tokens.get(i); - if (nv.equals("=")) - return false; - value.add(nv); - ++i; - } - if (hm.containsKey(key)) - return false; - hm.put(key, value); - } - return true; - } - public boolean init(String parameters) - { - String delims = "\t =;"; - StringTokenizer st = new StringTokenizer(parameters, delims, true); - - ArrayList tokens = new ArrayList (); - while (st.hasMoreTokens()) { - String nt = st.nextToken(); - if (delims.indexOf(nt) >= 0) { - if (nt.equals("=") || nt.equals(";")) - tokens.add(nt); - } else - tokens.add(nt); - - } - return init(tokens); - } - public ArrayList getValue(String variable) - { - return hm.get(variable); - } - public String getValueAsString(String variable, String defaultValue) - { - ArrayList ret = getValue(variable); - if (ret != null) { - String rets = ""; - for (String s : ret) - rets += " " + s; - return rets.substring(Math.min(1, rets.length())); // remove first space - } - return defaultValue; - } - public int getNumberOfValues(String variable) - { - ArrayList ret = getValue(variable); - if (ret != null) - return ret.size(); - else - return 0; - } - public String getValueAsString(String variable, int index, String defaultValue) - { - ArrayList ret = getValue(variable); - if (ret != null && index < ret.size()) - return ret.get(index); - return defaultValue; - } - public ArrayList getValuesAsList(String variable) - { - return getValuesAsList(variable, 1, null); - } - - public ArrayList getValuesAsList(String variable, int size, String defaultValue) - { - ArrayList ret = new ArrayList(); - if (getNumberOfValues(variable) == 1 && getValueAsString(variable, null).startsWith("file:")) { - ArrayList> matrix = Input.loadTable(getValueAsString(variable, null).substring(5), "[\t ]"); - if (matrix.size() == 1) - ret.addAll(matrix.get(0)); - if (matrix.size() > 1) { // transpose matrix - ArrayList tp = new ArrayList(); - boolean ok = true; -// System.err.println("Reading matrix"); - for (ArrayList als : matrix) { - if (als.size() != 1) - ok = false; - else - tp.add(als.get(0)); - } - if (ok) - ret.addAll(tp); -// System.err.println("Reading matrix ok" + ok); - } - } else - for (int i = 0; i < getNumberOfValues(variable); ++i) - ret.add(getValueAsString(variable, i, null)); - if (ret.size() == 0) - ret.add(defaultValue); - if (ret.size() == 1) - for (int i = 0; i < size - 1; ++i) - ret.add(new String(ret.get(0))); - return ret; - } - - - public ArrayList> getValuesAsMatrix(String variable) - { - if (getNumberOfValues(variable) == 1 && getValueAsString(variable, null).startsWith("file:")) { -// System.err.println("loading matrix " + getValueAsString(variable, null).substring(5)); - ArrayList> matrix = Input.loadTable(getValueAsString(variable, null).substring(5), "[\t ]"); - return matrix; - } - ArrayList> ret = new ArrayList>(); - if (getNumberOfValues(variable) > 0) - ret.add(new ArrayList()); - int line = 0; - - for (int i = 0; i < getNumberOfValues(variable); ++i) { - if (getValueAsString(variable, i, null).equals(";")) { - ++line; - ret.add(new ArrayList()); - } else - ret.get(line).add(getValueAsString(variable, i, null)); - } - return ret; - } - public boolean warning(String keyWords[]) - { - HashMap hm2 = new HashMap(); - - for (String s : hm.keySet()) - hm2.put(s, 1); - - for (String s : keyWords) - hm2.remove(s); - - if (hm2.size() > 0) { - System.err.println("Error! Unknown parameters: "); - for (String s : hm2.keySet()) - System.err.print(s + " "); - System.err.println(); - System.exit(-1); - } - - return false; - } - - public static void main(String args[]) - { - ParameterParser pp = new ParameterParser("a=40 10 30 b=10 d = 1 2;3 4 6 e=file:ped1"); - System.err.println("a = " + pp.getValueAsString("a", 0, "null")); - - System.err.println("a = " + pp.getValue("a")); - System.err.println("b = " + pp.getValue("b")); - System.err.println("c = " + pp.getValue("c")); - System.err.println("d = " + pp.getValue("d")); - System.err.println("number of values for a = " + pp.getNumberOfValues("a")); - System.err.println("number of values for b = " + pp.getNumberOfValues("b")); - System.err.println("number of values for c = " + pp.getNumberOfValues("c")); - System.err.println(pp.getValuesAsList("d")); - System.err.println(pp.getValuesAsMatrix("d")); - System.err.println(pp.getValuesAsMatrix("e")); - } - -} diff --git a/software/LepAnchor/src/PlaceAndOrientContigs.java b/software/LepAnchor/src/PlaceAndOrientContigs.java deleted file mode 100644 index 551edd3..0000000 --- a/software/LepAnchor/src/PlaceAndOrientContigs.java +++ /dev/null @@ -1,4821 +0,0 @@ -/** - This file is part of Lep-Anchor. - - Lep-Anchor is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Lep-Anchor is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Lep-Anchor. If not, see . - - Copyright (C) 2019 Pasi Rastas, pasi.rastas@gmail.com, University of Helsinki - -*/ - -import java.io.BufferedReader; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -public class PlaceAndOrientContigs { - - private ArrayList bed = null; // store bed file - private HashMap> bedHash = new HashMap>(); // get intervals for each contig - - private HashMap> haplotypeHash = new HashMap>(); // get intervals for each haplotype contig - - private HashMap chainLinkHash = new HashMap(); // get score for each chain link... - private HashMap chainLinkHashCut1 = new HashMap(); // get cut point for each link - private HashMap chainLinkHashCut2 = new HashMap(); // get cut point for each link - - private HashMap chainLinkHashCut1a = new HashMap(); // get alternative cut point for each link - private HashMap chainLinkHashCut2a = new HashMap(); // get alternative cut point for each link - - private HashMap chainHaplotypeHash = new HashMap(); // get score for each contig pair for second being haplotype of the first... - private HashMap chainHaplotypeCut = new HashMap(); // get haplotype alignment end points - - private HashMap scaffoldingLink_tmp = new HashMap(); // get scaffolding score linking contigs... - private HashMap scaffoldingLink = new HashMap(); // get scaffolding score linking contigs... - - private HashMap scaffoldingLinkInfo = new HashMap(); // get scaffolding score linking contigs... - - private ArrayList contigs = new ArrayList(); // store contigs - - //private HashMap> pafCoverage = new HashMap>(); - - private int numErrorsPerContig = 3; - private int numErrors = 40; - - private int numMaps = 0; - private ArrayList intervalsInChr = null; - - private double scaleScore = 0.00001; - private double orientationPenalty = 0.5; - private double cutPenalty = 0.001; - - private double liftoverScoreDiff = 0.5; - - private boolean useChainAndPaf = false; - - private String printAnimation = ""; - - private int maxBridge = 50000; - private int maxIntersect = 2000; - - private int numRuns = 5; - - private int numThreads = 8; - - private int minHaplotypeAlignmentScore = -10; - private int minLinkAlignmentScore = -10; - private boolean keepEmptyIntervals; - - private boolean commentOutput; - private boolean compressMap; - - private Proximity prox = null; - - - private void setCommentOutput(boolean commentOutput) { - this.commentOutput = commentOutput; - } - public void setCompressMap(boolean value) { - compressMap = value; - } - public void setPrintAnimation(String fn) { - printAnimation = fn; - } - - public void setUseChainAndPaf(boolean value) { - useChainAndPaf = value; - } - - public void setNumRuns(int runs) { - numRuns = runs; - } - - public void setNumErrors(int errors) { - numErrors = errors; - } - - public void setNumErrorsPerContig(int errors) { - numErrorsPerContig = errors; - } - - public void setKeepEmptyIntervals(boolean value) { - keepEmptyIntervals = value; - } - - public ArrayList> getMarkers(ContigInterval ci_result){ - ArrayList> ret = new ArrayList>(); - for (int map = 0; map < numMaps; ++map) - ret.add(new ArrayList()); - - ArrayList cis = bedHash.get(ci_result.getContig()); - if (cis != null) { - for (ContigInterval c2 : cis) { - if (Misc.intersectIntervals(c2.getStart(), c2.getEnd(), ci_result.getStart(), ci_result.getEnd())) - for (int map = 0; map < numMaps; ++map) - ret.get(map).addAll(c2.getMarkers(map, ci_result.getOrientation())); - } - } - return ret; - } - - // forward calculation, increasing map positions - private void forward1(ArrayList markers, int S[][], int P[][], int startBin, int endBin) { - forward1(markers, S, P, startBin, endBin, 0, markers.size()); - } - - private void forward1(ArrayList markers, int S[][], int P[][], int startBin, int endBin, int start, int end) - { - for (int mi = start; mi < end; ++mi) { - Marker m = markers.get(mi); - - //substract startBin - int maxI = 0; - int Si[] = S[mi]; - int Si1[] = S[mi + 1]; - int Pi1[] = P[mi + 1]; - for (int b = 0; b <= endBin - startBin; ++b) { - if (Si[b] > Si[maxI]) - maxI = b; - Si1[b] = Si[maxI];// + m.inside(b + startBin); - Pi1[b] = maxI + startBin; - } - int b = startBin; - while (b <= endBin) { - Si1[b - startBin] += m.inside(b); - b = m.nextInside(b); - } - } - } - - private void forwardFast1(ArrayList markers, int S[][], int startBin, int endBin) { - forwardFast1(markers, S, startBin, endBin, 0, markers.size()); - } - - private void forwardFast1(ArrayList markers, int S[][], int startBin, int endBin, int start, int end) - { - for (int mi = start; mi < end; ++mi) { - Marker m = markers.get(mi); - - //substract startBin - int maxS = 0; - int Si[] = S[mi]; - int Si1[] = S[mi + 1]; - for (int b = 0; b <= endBin - startBin; ++b) { - int s = Si[b]; - if (s > maxS) - maxS = s; - Si1[b] = maxS;// + m.inside(b + startBin); - } - int b = startBin; - while (b <= endBin) { - Si1[b - startBin] += m.inside(b); - b = m.nextInside(b); - } - - } - } - - private void forward2(ArrayList markers, int S[][], int P[][], int startBin, int endBin) { - forward2(markers, S, P, startBin, endBin, 0, markers.size()); - } - // forward calculation, decreasing map positions - private void forward2(ArrayList markers, int S2[][], int P2[][], int startBin, int endBin, int start, int end) - { - for (int mi = start; mi < end; ++mi) { - Marker m = markers.get(mi); - int maxI2 = endBin - startBin; - int S2i[] = S2[mi]; - int S2i1[] = S2[mi + 1]; - int P2i1[] = P2[mi + 1]; - - for (int b = endBin - startBin; b >= 0; --b) { - - if (S2i[b] > S2i[maxI2]) - maxI2 = b; - - S2i1[b] = S2i[maxI2];// + m.inside(b + startBin); // add startBin as we subtract it from b - P2i1[b] = maxI2 + startBin; - } - int b = startBin; - while (b <= endBin) { - S2i1[b - startBin] += m.inside(b); - b = m.nextInside(b); - } - - } - } - - private void forwardFast2(ArrayList markers, int S[][], int startBin, int endBin) { - forwardFast2(markers, S, startBin, endBin, 0, markers.size()); - } - // forward calculation, decreasing map positions - private void forwardFast2(ArrayList markers, int S2[][], int startBin, int endBin, int start, int end) - { - for (int mi = start; mi < end; ++mi) { - Marker m = markers.get(mi); - int maxS = 0; - int S2i[] = S2[mi]; - int S2i1[] = S2[mi + 1]; - - for (int b = endBin - startBin; b >= 0; --b) { - int s = S2i[b]; - if (s > maxS) - maxS = s; - S2i1[b] = maxS;// + m.inside(b + startBin); // add startBin as we subtract it from b - } - int b = startBin; - while (b <= endBin) { - S2i1[b - startBin] += m.inside(b); - b = m.nextInside(b); - } - - } - } - - //positions increasing - private int solveForward(ArrayList markers) { - return solveForward(markers, getMinBin(markers), getMaxBin(markers)); - } - private int solveForward(ArrayList markers, int startBin, int endBin) { - //System.err.println(startBin); - //System.err.println(endBin); - - int numMarkers = markers.size(); - int S[][] = new int[numMarkers + 1][endBin - startBin + 1]; - int P[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - forward1(markers, S, P, startBin, endBin); - - int maxI = startBin; - for (int i = startBin; i <= endBin; ++i) { - if (S[numMarkers][i - startBin] > S[numMarkers][maxI - startBin]) - maxI = i; - } - - int ret = S[numMarkers][maxI - startBin]; - - // + orientation - for (int mi = numMarkers; mi > 0; --mi) { - markers.get(mi - 1).pPlus = maxI; - //System.err.println(markers.get(mi - 1) + "\t" + maxI); - maxI = P[mi][maxI - startBin]; - } - return ret; - } - private int solveForwardFast(ArrayList markers) { - return solveForwardFast(markers, getMinBin(markers), getMaxBin(markers)); - } - private int solveForwardFast(ArrayList markers, int startBin, int endBin) { - //System.err.println(startBin); - //System.err.println(endBin); - - int numMarkers = markers.size(); - int S[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - forwardFast1(markers, S, startBin, endBin); - - int ret = 0; - int SnM[] = S[numMarkers]; - for (int b = startBin; b <= endBin; ++b) { - int s = SnM[b - startBin]; - if (s > ret) - ret = s; - } - - return ret; - } - - - //positions decreasing - private int solveBackward(ArrayList markers) { - return solveBackward(markers, getMinBin(markers), getMaxBin(markers)); - } - private int solveBackward(ArrayList markers, int startBin, int endBin) { - //System.err.println(startBin); - //System.err.println(endBin); - - int numMarkers = markers.size(); - - int S2[][] = new int[numMarkers + 1][endBin - startBin + 1]; - int P2[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - forward2(markers, S2, P2, startBin, endBin); - - int maxI2 = endBin; - for (int i = endBin; i >= startBin; --i) { - if (S2[numMarkers][i - startBin] > S2[numMarkers][maxI2 - startBin]) - maxI2 = i; - } - - int ret = S2[numMarkers][maxI2 - startBin]; - - // - orientation - for (int mi = numMarkers; mi > 0; --mi) { - markers.get(mi - 1).pMinus = maxI2; - //System.err.println(markers.get(mi - 1) + "\t" + maxI2); - maxI2 = P2[mi][maxI2 - startBin]; - } - return ret; - } - - public static int[] markerScore(ArrayList markers) - { - PlaceAndOrientContigs poc = new PlaceAndOrientContigs(); - return poc.solve(markers); - } - - private int[] solve(ArrayList markers) - { - int scorePlus = solveForward(markers); - int scoreMinus = solveBackward(markers); - int ret[] = new int[]{scorePlus, scoreMinus}; - return ret; - } - - public void loadBed(String fn, int chromosome) - { - bed = InputData.loadBed(fn, chromosome); - for (ContigInterval ci : bed) { - if (!bedHash.containsKey(ci.getContig())) { - bedHash.put(ci.getContig(), new ArrayList()); - contigs.add(ci.getContig()); - } - bedHash.get(ci.getContig()).add(ci); - } - for (String c: contigs) { - ArrayList aci = bedHash.get(c); - Collections.sort(aci); // Put positions into physical order - long prev = 0; - for (ContigInterval ci: aci) { - if (ci.getStart() > 0 && ci.getStart() > ci.getEnd() || ci.getStart() <= prev) { // check the consistency of the bed... - System.err.println(c + "\t" + ci.getStart() + "\t" + ci.getEnd()); - System.err.println("Error: erroneous or overlapping intervals in the bed file"); - System.exit(-1); - } - prev = ci.getEnd(); - } - } - System.err.println(contigs.size() + " contigs loaded from the bed file"); - //TODO: - } - - //reverse map - private void flip(ArrayList markers) { - int max = getMaxBin(markers); - for (Marker m : markers) - m.flip(max); - } - - private long[] chain2OneBase(String length, String orientation, String start, String stop) - { - if ("+".equals(orientation)) { - return new long[]{Long.parseLong(start) + 1, Long.parseLong(stop)}; - } - else { - long l = Long.parseLong(length); - return new long[]{l - Long.parseLong(stop) + 1, l - Long.parseLong(start)}; - } - } - - // first coordinate system to second - private long mapPosition12(ArrayList alignment, long pos1, boolean sameOrientation){ - int low = 0; - int high = alignment.size() - 1; - - while (low <= high) { - int mid = (low + high) / 2; - long a[] = alignment.get(mid); - long mpos = a[0]; - long len = a[2]; - - if (mpos > pos1) - high = mid - 1; - else if (mpos <= pos1 && mpos + len > pos1) - return a[1] + (sameOrientation ? (pos1 - mpos) : (mpos - pos1)); - else if (mpos < pos1) - low = mid + 1; - else - return -1; - } - return -1; - } - - // second coordinate system to first - private long mapPosition21(ArrayList alignment, long pos2, boolean sameOrientation){ - int low = 0; - int high = alignment.size() - 1; - - while (low <= high) { - int mid = (low + high) / 2; - long a[] = alignment.get(mid); - long mpos = a[1]; - long len = a[2]; - if (sameOrientation) { - if (mpos > pos2) - high = mid - 1; - else if (mpos <= pos2 && mpos + len > pos2) - return a[0] + (pos2 - mpos); - else if (mpos < pos2) - low = mid + 1; - else - return -1; - } else { - if (mpos < pos2) - high = mid - 1; - else if (mpos >= pos2 && mpos - len < pos2) - return a[0] + (pos2 - mpos); - else if (mpos > pos2) - low = mid + 1; - else - return -1; - } - } - return -1; - } - - //c2 is full haplotype of ofHaplotype? - private int calculateLiftOverHaplotype(ContigInterval ofHaplotype, int ofHaplotypeOrientation1, ContigInterval c2, ArrayList alignment, boolean sameOrientation) - { - int score = 0; - for (int map = 0; map < numMaps; ++map) { - int numM2 = c2.getMarkers(map).size(); - - if (numM2 > 0) { - ArrayList markers = new ArrayList(); - markers.addAll(ofHaplotype.getMarkers(map, ofHaplotypeOrientation1)); - - int score1 = solveForward(markers); - - ArrayList markers2 = c2.getMarkers(map); - for (Marker m: markers2) { - long p21 = mapPosition21(alignment, m.getPosition(), sameOrientation); - if (p21 > 0) - markers.add(new Marker(m, p21)); - } - Collections.sort(markers); - if (ofHaplotypeOrientation1 != 0) - Collections.reverse(markers); - - score += solveForward(markers) - score1; - } - } - return score; - } - - private int[] calculateLiftOver(ContigInterval c1, int orientation1, ContigInterval c2, int orientation2, ArrayList alignment, boolean sameOrientation, long interval1[], long interval2[]) - { - int score1 = 0; - int score2 = 0; - for (int map = 0; map < numMaps; ++map) { - int numM1 = c1.getMarkers(map).size(); - int numM2 = c2.getMarkers(map).size(); - if (numM2 > 0 || numM1 > 0){ // markers in one or both contigs... - int start1 = 0; - int end1 = numM1 - 1; - if (orientation1 != 0) { - end1 = 0; - start1 = numM1 - 1; - } - - int start2 = 0; - int end2 = numM2 - 1; - if (orientation2 != 0) { - end2 = 0; - start2 = numM2 - 1; - } - - // c1|----------------| - // ||||| (interval 1&2 markers put to am1 and am2) - // c2|-------------| - - ArrayList am1 = new ArrayList(); - ArrayList am2 = new ArrayList(); - - while (orientation1 == 0 && end1 >= start1 && c1.getMarker(map, end1).position >= interval1[0]) { - if (c1.getMarker(map, end1).position <= interval1[1]) - am1.add(c1.getMarker(map, end1)); - --end1; - } - - while (orientation1 != 0 && end1 <= start1 && c1.getMarker(map, end1).position <= interval1[1]) { - if (c1.getMarker(map, end1).position >= interval1[0]) - am1.add(c1.getMarker(map, end1)); - ++end1; - } - - while (orientation2 == 0 && end2 >= start2 && c2.getMarker(map, start2).position <= interval2[1]) { - if (c2.getMarker(map, start2).position >= interval2[0]) - am2.add(c2.getMarker(map, start2)); - ++start2; - } - - while (orientation2 != 0 && end2 <= start2 && c2.getMarker(map, start2).position >= interval2[0]) { - if (c2.getMarker(map, start2).position <= interval2[1]) - am2.add(c2.getMarker(map, start2)); - --start2; - } - - ArrayList test1 = new ArrayList(); - if (orientation1 == 0) - for (int i = start1; i <= end1; ++i) - test1.add(c1.getMarker(map, i)); - else - for (int i = start1; i >= end1; --i) - test1.add(c1.getMarker(map, i)); - - //DO MAGIC HERE... - Collections.reverse(am1); - - ArrayList mid1 = new ArrayList(); - ArrayList mid2 = new ArrayList(); - ArrayList merge1 = new ArrayList(); - ArrayList merge2 = new ArrayList(); - - //merge1 - for (Marker m : am1) { - long p2 = mapPosition12(alignment, m.getPosition(), sameOrientation); - if (p2 > 0) { // liftover possible - Marker m2 = new Marker(m, p2); - mid2.add(m2); - } - } - merge1.addAll(mid2); - merge1.addAll(am2); - Collections.sort(merge1); - if (orientation2 != 0) - Collections.reverse(merge1); - - //merge2 - for (Marker m : am2) { - long p1 = mapPosition21(alignment, m.getPosition(), sameOrientation); - if (p1 > 0) { // liftover possible - Marker m2 = new Marker(m, p1); - mid1.add(m2); - } - } - - merge2.addAll(mid1); - merge2.addAll(am1); - Collections.sort(merge2); - - if (orientation1 != 0) - Collections.reverse(merge2); - - //System.err.println(numM2 + "\t" + start2 + "\t" + end2 + "\t" + numM1 + "\t" + start1 + "\t" + end1 ); - - ArrayList test3 = new ArrayList(); - if (orientation2 == 0) - for (int i = start2; i <= end2; ++i) - test3.add(c2.getMarker(map, i)); - else - for (int i = start2; i >= end2; --i) - test3.add(c2.getMarker(map, i)); - - ArrayList test = new ArrayList(); - - // c1 + c2 - test.addAll(c1.getMarkers(map, orientation1)); - test.addAll(c2.getMarkers(map, orientation2)); - int s0 = solveForward(test); - - score1 -= s0; - score2 -= s0; - - //System.err.println(solve(test)[0] + "\t(" + test.size() + ")\t"); - - test.clear(); - test.addAll(test1); - test.addAll(merge1); - test.addAll(test3); - - // c1 + merge1 + c2 - int s1 = solveForward(test); - score1 += s1; - - //System.err.print(c1.getContig() + "_" + orientation1 + "\t" + c2.getContig() + "_" + orientation2); - - //System.err.print("\tscores\t" + solve(test)[0] + "\t(" + test.size() + ")\t"); - - test.clear(); - test.addAll(test1); - test.addAll(merge2); - test.addAll(test3); - //System.err.print(solve(test)[0] + "\t(" + test.size() + ")\t"); - - // c1 + merge2 + c2 - int s2 = solveForward(test); - score2 += s2; - - //System.out.println(s0 + "\t" + s1 + "\t" + s2); - - } - } - return new int[]{score1, score2}; - } - - // change alignment from contig1=>contig2 to contig2=>contig1 - private ArrayList reverseAlignment(ArrayList a, boolean sameOrientation){ - ArrayList ret = new ArrayList(); - if (sameOrientation) { - for (long[] aa : a) - ret.add(new long[]{aa[1],aa[0], aa[2]}); - } else { - int n = a.size(); - for (int i = n - 1; i>=0; --i) { // reverse and add length for each block - long[] aa = a.get(i); - ret.add(new long[]{aa[1] - aa[2] + 1, aa[0] + aa[2] - 1, aa[2]}); - } - } - return ret; - } - - public void loadProximity(String fn, int bin, int maxDistance, double scale){ - prox = new Proximity(bin, maxDistance, scale); - boolean nempty = prox.loadData(fn, bedHash); - if (!nempty) - prox = null; - } - - public void loadPaf(String fn, int maxScore, double scalePaf){ - System.err.println("loading paf..."); - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - - ArrayList> rows = new ArrayList>(); - - do { - ArrayList row = Input.loadTableRow(br, "\t "); - if (row == null) - break; - - if (row.size() < 9) { - System.err.println("Warning: skipping row " + row); - continue; - } - - if (!bedHash.containsKey(row.get(0)) && bedHash.containsKey(row.get(5))) { // find "extra" contigs or single reads for scaffolding... - if (rows.size() == 0 || rows.get(0).get(0).equals(row.get(0))) - rows.add(row); - else { - processPAF(rows); - rows.clear(); - rows.add(row); - } - } - - } while (true); - processPAF(rows); - System.err.println("Scaffolding links:"); - for (String key: scaffoldingLink.keySet()) { - int value = (int)(0.5 + scalePaf * scaffoldingLink.get(key)); - if (value > maxScore) { - value = maxScore; - } - scaffoldingLink.put(key, value); - System.err.println(key + "\t" + value); - } - - br.close(); - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - } - } - - //int tmps = 0; - - private void helperPAF1(ContigInterval ci1, ContigInterval ci2, ArrayList row1, ArrayList row2) { - - //check that alignments are in right order in the read... - - long rstart1 = Long.parseLong(row1.get(2)); - long rend1 = Long.parseLong(row1.get(3)); - - long rstart2 = Long.parseLong(row2.get(2)); - long rend2 = Long.parseLong(row2.get(3)); - - // require non-intersecting intervals... - if (Misc.intersectIntervalsLength(rstart1, rend1, rstart2, rend2) > maxIntersect) - return; - - boolean plus = row1.get(4).equals("+"); - boolean r1first = rstart1 < rstart2; - - long start1 = Long.parseLong(row1.get(7)); - long end1 = Long.parseLong(row1.get(8)); - - long start2 = Long.parseLong(row2.get(7)); - long end2 = Long.parseLong(row2.get(8)); - - //++ orientation - long gap1 = ci1.calculateGapLength(0, start1 + 1, end1); - long gap2 = ci2.calculateGapLength(1, start2 + 1, end2); - - long length1 = end1 - start1; - long length2 = end2 - start2; - - if (length1 - gap1 > 0 && length2 - gap2 > 0 ) { - if (plus ^ !r1first) { - //System.err.println(ci1.getContig() + "->" + ci2.getContig()); - String key = ci1.toString() + '+' + ci2.toString() + '+'; - scaffoldingLink_tmp.put(key, 1); - scaffoldingLinkInfo.put(key, row1.get(0)); - } else { // read is not consistent - //System.err.println("*"); - //System.err.println(row1); - //System.err.println(ci1); - //System.err.println(row2); - //System.err.println(ci2); - //System.err.println("*"); - } - } - //-- orientation - gap1 = ci1.calculateGapLength(1, start1 + 1, end1); - gap2 = ci2.calculateGapLength(0, start2 + 1, end2); - - if (length1 - gap1 > 0 && length2 - gap2 > 0) { - if (plus ^ r1first) { - //System.err.println(ci1.getContig() + "->" + ci2.getContig()); - String key = ci1.toString() + '-' + ci2.toString() + '-'; - scaffoldingLink_tmp.put(key, 1); - scaffoldingLinkInfo.put(key, row1.get(0)); - - } else { // read is not consistent - //System.err.println("*"); - //System.err.println(row1); - //System.err.println(ci1); - //System.err.println(row2); - //qSystem.err.println(ci2); - //System.err.println("*"); - } - } - } - - private void helperPAF2(ContigInterval ci1, ContigInterval ci2, ArrayList row1, ArrayList row2) { - //check that alignments are in right order in the read... - long rstart1 = Long.parseLong(row1.get(2)); - long rend1 = Long.parseLong(row1.get(3)); - long rstart2 = Long.parseLong(row2.get(2)); - long rend2 = Long.parseLong(row2.get(3)); - - // require non-intersecting intervals... - if (Misc.intersectIntervalsLength(rstart1, rend1, rstart2, rend2) > maxIntersect) - return; - - boolean plus = row1.get(4).equals("+"); - boolean r1first = rstart1 < rstart2; - - long start1 = Long.parseLong(row1.get(7)); - long end1 = Long.parseLong(row1.get(8)); - - long start2 = Long.parseLong(row2.get(7)); - long end2 = Long.parseLong(row2.get(8)); - - //+- orientation - long gap1 = ci1.calculateGapLength(0, start1 + 1, end1); - long gap2 = ci2.calculateGapLength(0, start2 + 1, end2); - - long length1 = end1 - start1; - long length2 = end2 - start2; - - if (length1 - gap1 > 0 && length2 - gap2 > 0) { - if (plus ^ !r1first) { - //System.err.println(ci1.getContig() + "->" + ci2.getContig()); - String key = ci1.toString() + '+' + ci2.toString() + '-'; - scaffoldingLink_tmp.put(key, 1); - scaffoldingLinkInfo.put(key, row1.get(0)); - } else { // read is not consistent - //System.err.println("*"); - //System.err.println(row1); - //System.err.println(ci1); - //System.err.println(row2); - //System.err.println(ci2); - //System.err.println("*"); - } - } - //-+ orientation - gap1 = ci1.calculateGapLength(1, start1 + 1, end1); - gap2 = ci2.calculateGapLength(1, start2 + 1, end2); - - if (length1 - gap1 > 0 && length2 - gap2 > 0) { - if (plus ^ r1first) { - //System.err.println(ci1.getContig() + "->" + ci2.getContig()); - String key = ci1.toString() + '-' + ci2.toString() + '+'; - scaffoldingLink_tmp.put(key, 1); - scaffoldingLinkInfo.put(key, row1.get(0)); - - } else { // read is not consistent - //System.err.println("*"); - //System.err.println(row1); - //System.err.println(ci1); - //System.err.println(row2); - //System.err.println(ci2); - //System.err.println("*"); - } - } - } - - private void processPAF(ArrayList> rows) - { - HashMap>> alignmentHash = new HashMap>>(); - for (ArrayList row : rows) { - ArrayList> a = alignmentHash.get(row.get(5)); - if (a == null) { - a = new ArrayList>(); - alignmentHash.put(row.get(5), a); - } - a.add(row); - } - ArrayList contigs = new ArrayList(); - - contigs.addAll(alignmentHash.keySet()); - - int numContigs = contigs.size(); - - for (int c1 = 0; c1 < numContigs; ++c1) { - String contig1 = contigs.get(c1); - //for (int c2 = c1 + 1; c2 < numContigs; ++c2) { - for (int c2 = c1; c2 < numContigs; ++c2) { //allow links between same contig... - - if (c1 == c2 && bedHash.get(contig1).size() <= 1) // no need to go further if only one possible ContigInterval and contig... - continue; - - String contig2 = contigs.get(c2); - - - ArrayList> rows1 = alignmentHash.get(contig1); - ArrayList> rows2 = alignmentHash.get(contig2); - - - for (ArrayList row1 : rows1) - for (ArrayList row2 : rows2) { - long rstart1 = Long.parseLong(row1.get(2)); - long rend1 = Long.parseLong(row1.get(3)); - long rstart2 = Long.parseLong(row2.get(2)); - long rend2 = Long.parseLong(row2.get(3)); - - // require non-intersecting intervals... - long il = Misc.intersectIntervalsLength(rstart1, rend1, rstart2, rend2); - - if (il > maxIntersect || il > 0.5 * Math.min(rend1 - rstart1, rend2 - rstart2)) - continue; - - //TODO: Take into account useChainAndPaf in the calculation of bridgeLength and other... - //TODO: Have to take into account the contig overlap... - - //read [rs1 re1] [rs2 re2], bridgeLength = rs2-re1 - //read [rs2 re2] [rs1 re1], bridgeLength = rs1-re2 - long bridgeLength = Math.max(rstart2 - rend1, rstart1 - rend2); - if (bridgeLength > maxBridge) - continue; - - if (row1.get(4).equals(row2.get(4))) { // same orientation - for (ContigInterval ci1 : bedHash.get(contig1)) { - for (ContigInterval ci2 : bedHash.get(contig2)) { - if (ci1 != ci2) { - helperPAF1(ci1, ci2, row1, row2); - helperPAF1(ci2, ci1, row2, row1); - } - } - } - - } else { // different orientation - for (ContigInterval ci1 : bedHash.get(contig1)) { - for (ContigInterval ci2 : bedHash.get(contig2)) { - if (ci1 != ci2) { - helperPAF2(ci1, ci2, row1, row2); - helperPAF2(ci2, ci1, row2, row1); - } - } - } - } - } - } - } - - for (String key : scaffoldingLink_tmp.keySet()) { - Integer os = scaffoldingLink.get(key); - if (os == null) - scaffoldingLink.put(key, scaffoldingLink_tmp.get(key)); - else - scaffoldingLink.put(key, os + scaffoldingLink_tmp.get(key)); - } - scaffoldingLink_tmp.clear(); - - //if (numContigs > 1) - // System.err.println(numContigs + "\t" + (++tmps)); - - //calculate coverage for each contig (reads spanning each position...) - } - - private void addHaplotype(ContigInterval ofHaplotype, int ofHaplotypeOrientation, ContigInterval haplotype, long ha[], double ascore, ArrayList alignment, boolean sameOrientation) - { - - int haplotypeScore = (int) (ascore - haplotype.calculateGapLengthHaplotype(ha[0], ha[1]) * cutPenalty + 0.5); //+0.5 = rounding - if (haplotypeScore >= minHaplotypeAlignmentScore) { - haplotypeScore += calculateLiftOverHaplotype(ofHaplotype, ofHaplotypeOrientation, haplotype, alignment, sameOrientation); - if (haplotypeScore > 0) { - String key = ofHaplotype.toString() + ((ofHaplotypeOrientation == 0) ? '+':'-') + haplotype.toString(); - Integer oscore = chainHaplotypeHash.get(key); - if (oscore == null || oscore < haplotypeScore) { - chainHaplotypeHash.put(key, (int) haplotypeScore); - chainHaplotypeCut.put(key, ha); - } - - //System.err.println(key + "\t" + (ascore * scaleScore - calculateGapLengthHaplotype(ci2, p2[0], p2[1]) * cutPenalty) + "\t" + haplotypeScore1 + "\thaplotype"); - } - } - } - - // add score between contigs ci1 and ci2 in these orientation based on alignment chain... - private void addChainLink(ContigInterval ci1, int orientation1, ContigInterval ci2, int orientation2, long ai1[], long ai2[], double ascore, ArrayList alignment, boolean sameOrientation) - { - long l1 = ci1.calculateGapLength(orientation1, ai1[0], ai1[1]); - long l2 = ci2.calculateGapLength(1 - orientation2, ai2[0], ai2[1]); - - double score = ascore; - if ((orientation1 == orientation2) != sameOrientation) - score *= orientationPenalty; - - score -= (l1 + l2) * cutPenalty; - - if (score >= minLinkAlignmentScore) { // is this needed? - int los[] = calculateLiftOver(ci1, orientation1, ci2, orientation2, alignment, sameOrientation, ai1, ai2); - int max1 = 0; - if (los[1] > los[0]) - max1 = 1; - - score += los[max1]; - - if (score >= 0.5) { - String key = ci1.toString() + ((orientation1 == 0) ? '+':'-') + ci2.toString() + ((orientation2 == 0) ? '+':'-'); - //System.err.println(key + "\t" + score1); - Integer oldScore = chainLinkHash.get(key); - score = (int)(score + 0.5); // round up... - if (oldScore == null || oldScore < score) { - chainLinkHash.put(key, (int) score); - - //if ((orientation1 == orientation2) != sameOrientation) { // orientation do not match... make a palindrome... - // long start1 = (orientation1 == 0) ? ai1[1] : ai1[0]; - // chainLinkHashCut1.put(key, start1); - // chainLinkHashCut1a.put(key, start1); - // long end2 = (orientation2 == 0) ? ai2[0] : ai2[1]; - // chainLinkHashCut2.put(key, end2); - // chainLinkHashCut2a.put(key, end2); - //} else - { - if (orientation1 == 0) { // + orientation - if (max1 == 1) { - chainLinkHashCut1.put(key, ai1[1]); // take whole alignment - chainLinkHashCut1a.put(key, ai1[0] - 1); - } - else { - chainLinkHashCut1.put(key, ai1[0] - 1); // cut just before alignment - chainLinkHashCut1a.put(key, ai1[1]); - } - } - else { // - orientation - if (max1 == 1) { - chainLinkHashCut1.put(key, ai1[0]); // whole - chainLinkHashCut1a.put(key, ai1[1] + 1); - } - else { - chainLinkHashCut1.put(key, ai1[1] + 1); // after - chainLinkHashCut1a.put(key, ai1[0]); - } - } - if (orientation2 == 0) { // + orientation - if (max1 == 1) { - chainLinkHashCut2.put(key, ai2[1] + 1); // after - chainLinkHashCut2a.put(key, ai2[0]); - } - else { - chainLinkHashCut2.put(key, ai2[0]); // whole - chainLinkHashCut2a.put(key, ai2[1] + 1); - } - } - else { // - orientation - if (max1 == 1) { - chainLinkHashCut2.put(key, ai2[0] - 1); // before - chainLinkHashCut2a.put(key, ai2[1]); - } - else { - chainLinkHashCut2.put(key, ai2[1]); // whole - chainLinkHashCut2a.put(key, ai2[0] - 1); - } - } - } - } - } - } - } - // Find largest index i, where array[i] <= value, array sorted ascending order - private int binarySearchR(ArrayList array, int value){ - int right = array.size() - 1; - if (right < 0) - return -1; - int left = 0; - - while (right > left + 1) { - int mid = (left + right) / 2; - int am = array.get(mid); - if (am > value) - right = mid - 1; - else - left = mid + ((am == value) ? 0 : 1); - } - if (array.get(right) <= value) - return right; - if (array.get(left) <= value) - return left; - return left - 1; - } - - // Find smallest index i, where array[i] >= value, array sorted ascending order - private int binarySearchL(ArrayList array, int value){ - int right = array.size() - 1; - if (right < 0) - return 0; - int left = 0; - - while (right > left + 1) { - int mid = (left + right) / 2; - int am = array.get(mid); - if (am < value) - left = mid + 1; - else - right = mid - ((am == value) ? 0 : 1); - } - if (array.get(left) >= value) - return left; - if (array.get(right) >= value) - return right; - return right + 1; - } - - //calculate how many markers in array between start and stop - // array sorted - private int calculateNumMarkers(ArrayList array, int start, int end) - { - return (binarySearchR(array, end) - binarySearchL(array, start) + 1); - } - - private long[] getStartEnd1(ArrayList alignment) - { - long first[] = alignment.get(0); - long last[] = alignment.get(alignment.size() - 1); - return new long[]{first[0], last[0] + last[2] - 1}; - } - - private long[] getStartEnd2(ArrayList alignment, boolean sameOrientation) - { - long first[] = alignment.get(0); - long last[] = alignment.get(alignment.size() - 1); - if (sameOrientation) - return new long[]{first[1], last[1] + last[2] - 1}; - else - return new long[]{last[1] - last[2] + 1, first[1]}; - } - - // - private double trimChain(ArrayList alignment, boolean sameOrientation, ContigInterval ci1, ContigInterval ci2, ArrayList ret) { - if (alignment.size() == 0) - return 0.0; - - long ap1[] = getStartEnd1(alignment); - long ap2[] = getStartEnd2(alignment, sameOrientation); - - // if contig intervals and alignment do not intersect, we are done here... - if (!Misc.intersectIntervals(ap1[0], ap1[1], ci1.getMinStart(), ci1.getMaxEnd()) || !Misc.intersectIntervals(ap2[0], ap2[1], ci2.getMinStart(), ci2.getMaxEnd())) - return 0.0; - long trimLength1[] = new long[2]; - - if (ap1[0] < ci1.getMinStart()) // alignment spans too far - trimLength1[0] = ci1.getMinStart() - ap1[0]; - if (ap1[1] > ci1.getMaxEnd()) // alignment spans too far - trimLength1[1] = ap1[1] - ci1.getMaxEnd(); - - long trimLength2[] = new long[2]; - - - if (ap2[0] < ci2.getMinStart()) // alignment spans too far - trimLength2[0] = ci2.getMinStart() - ap2[0]; - if (ap2[1] > ci2.getMaxEnd()) // alignment spans too far - trimLength2[1] = ap2[1] - ci2.getMaxEnd(); - - if (!sameOrientation) { - long tmp = trimLength2[0]; - trimLength2[0] = trimLength2[1]; - trimLength2[1] = tmp; - } - - long tl1 = trimLength1[0] + trimLength2[0]; - long tl2 = trimLength1[1] + trimLength2[1]; - if (tl1 + tl2 > 0) { - //System.err.println("Alignment of " + ci1.getContig() + " and " + ci2.getContig() + " needs trimming of " + (tl1 + tl2)); - - long first[] = alignment.get(0); - long last[] = alignment.get(alignment.size() - 1); - //System.err.println(first[0] + "\t" + first[1] + "\t" + last[0] + "\t" + last[1] + "\t" + last[2] + "\t" + ci1 + "\t" + ci2); - - long alignL = 0; - for (long[] a : alignment) - alignL += a[2]; - - int t1 = 0; - long wt1 = 0; - long totalW1 = 0; - if (tl1 > 0) { - for (long[] a : alignment) { - long w = a[2]; - long dt1 = a[0] - ap1[0]; - long dt2 = ((sameOrientation) ? a[1] - ap2[0] : ap2[1] - a[1]); - if (dt1 + w - 1 >= trimLength1[0] && dt2 + w - 1 >= trimLength2[0]) { - if (dt1 >= trimLength1[0] && dt2 >= trimLength2[0]) { - wt1 = 0; - //System.err.print("*full "); - } else { - wt1 = Math.max(trimLength1[0] - dt1, trimLength2[0] - dt2); - //System.err.print("*"+ wt1 + " of " + w + " "); - } - totalW1 += wt1; - //System.err.println("trimming " + t1 + " blocks (of " + alignment.size() + ") with length " + dt1 + "\t" + dt2 + "\t" + ((alignL - totalW1) / (double) alignL)); - - break; - } else - totalW1 += w; - ++t1; - } - } - - int t2 = 0; - long wt2 = 0; - long totalW2 = 0; - if (tl2 > 0) { - for (int ai = alignment.size() - 1; ai >= 0; --ai) { - long[] a = alignment.get(ai); - long w = a[2]; - - long p1 = a[0] + w - 1; - long p2 = ((sameOrientation) ? a[1] + w - 1 : a[1] - w + 1); - - long dt1 = ap1[1] - p1; - long dt2 = ((sameOrientation) ? ap2[1] - p2 : p2 - ap2[0]); - - if (dt1 + w - 1 >= trimLength1[1] && dt2 + w - 1 >= trimLength2[1]) { - if (dt1 >= trimLength1[1] && dt2 >= trimLength2[1]) { - wt2 = 0; - //System.err.print("full "); - } else { - wt2 = Math.max(trimLength1[1] - dt1, trimLength2[1] - dt2); - //System.err.print(wt2 + " of " + w + " "); - } - totalW2 += wt2; - //System.err.println("trimming " + t2 + " blocks (of " + alignment.size() + ") with length " + dt1 + "\t" + dt2 + "\t" + ((alignL - totalW2) / (double) alignL)); - - break; - } else - totalW2 += w; - ++t2; - } - } - if (totalW1 + totalW2 >= alignL) { - //System.err.println("all trimmed of a chain"); - return 0.0; // all trimmed off - } else { - ret.clear(); - int n = alignment.size(); - for (int i = t1; i < n - t2; ++i) { - long a[] = alignment.get(i); - if (i == t1 && wt1 > 0) { - ret.add(new long[]{a[0] + wt1, a[1] + ((sameOrientation) ? + wt1 : -wt1), a[2] - wt1}); - } else if (i == n - t2 - 1 && wt2 > 0) { - ret.add(new long[]{a[0], a[1], a[2] - wt2}); - } else - ret.add(a); - } - return (alignL - totalW1 - totalW2) / (double) alignL; - } - - } else { - //System.err.println("Alignment of " + ci1.getContig() + " and " + ci2.getContig() + " does not need trimming"); - return 1.0; - } - //total length of aligning bases... - } - - public void loadChain(String fn){ - int numAlignments = 0; - /* - //calculate data structure to get the number of marker in each interval in O(log(n)) time - - HashMap> markerHash = new HashMap>(); - for (ContigInterval ci : intervalsInChr) { - ArrayList markers = new ArrayList(); - markerHash.put(ci.getContig(), markers); - for (int map = 0; map < numMaps; ++map) - for (Marker m : ci.getMarkers(map)) - markers.add(m.getPosition()); - Collections.sort(markers); - }*/ - - System.err.println("loading alignment chain..."); - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - do { - ArrayList row = Input.loadTableRow(br, "\t "); - if (row == null) - break; - boolean skip = true; - if (row.size() >= 12 && "chain".equals(row.get(0))) { - String contig1 = row.get(2); - String contig2 = row.get(7); - - if (bedHash.containsKey(contig1) && bedHash.containsKey(contig2)) { - skip = false; - long p1_[] = chain2OneBase(row.get(3), row.get(4), row.get(5), row.get(6)); - long p2_[] = chain2OneBase(row.get(8), row.get(9), row.get(10), row.get(11)); - if ("-".equals(row.get(4))) { - System.err.println("Error: only ++, and +- orientation allowed in the chain file"); - continue; - } - double ascore_ = scaleScore * Double.parseDouble(row.get(1)); // alignment score - - //int numMarkers1 = calculateNumMarkers(markerHash.get(contig1), p1[0], p1[1]); - //int numMarkers2 = calculateNumMarkers(markerHash.get(contig2), p2[0], p2[1]); - //System.err.println(contig1 + "\t" + p1[0] + " " + p1[1] + " " + numMarkers1); - //System.err.println(contig2 + "\t" + p2[0] + " " + p2[1] + " " + numMarkers2); - - boolean sameOrientation = row.get(4).equals(row.get(9)); - - // load alignment for liftover... - ArrayList alignment_ = new ArrayList(); - ArrayList row2 = null; - long v1 = p1_[0]; - long v2 = ((sameOrientation) ? p2_[0] : p2_[1]); - int v3 = 0; - do { - row2 = Input.loadTableRow(br, "\t "); - - v3 = Integer.parseInt(row2.get(0)); - alignment_.add(new long[]{v1, v2, v3}); - v1 += v3; - v2 += ((sameOrientation) ? v3 : -v3); - if (row2.size() >= 3) { - v1 += Integer.parseInt(row2.get(1)); - v2 += ((sameOrientation) ? Integer.parseInt(row2.get(2)) : -Integer.parseInt(row2.get(2))); - } - } while (row2 != null && row2.size() >= 3); - - ArrayList revAlignment_ = reverseAlignment(alignment_, sameOrientation); - - //System.err.println(row); - //for (int[] a : alignment) - // System.err.println(a[0] + "\t" + a[1] + "\t" + a[2]); - - //for (int i = 1; i<=100; i+=1) - // System.err.println(mapPosition12(alignment, i, sameOrientation)); - //System.err.println("*"); - //for (int i = 1; i<=100; i+=1) - // System.err.println(mapPosition21(alignment, i, sameOrientation)); - - //if (true) - // System.exit(-1); - - //check here - - for (ContigInterval ci1 : bedHash.get(contig1)) - for (ContigInterval ci2 : bedHash.get(contig2)) { - - ArrayList alignment = new ArrayList(); - - double t = trimChain(alignment_, sameOrientation, ci1, ci2, alignment); - if (t == 0.0) - continue; - - ArrayList revAlignment = new ArrayList(); - long p1[]; - long p2[]; - double ascore = t * ascore_; - - if (t < 1.0) { - double t2 = trimChain(revAlignment_, sameOrientation, ci2, ci1, revAlignment); - if (t != t2) { - System.err.println("Error in parsing alignment chain:" + t +"!="+ t2); - System.exit(-1); - } - - p1 = getStartEnd1(alignment); - p2 = getStartEnd2(alignment, sameOrientation); - if (p2[0] > p2[1]) { - long tmp = p2[0]; - p2[0] = p2[1]; - p2[1] = tmp; - } - - } else { - alignment = alignment_; - revAlignment = revAlignment_; - p1 = p1_; - p2 = p2_; - } - for (int orientation1 = 0; orientation1 < 2; ++orientation1) { - //calculate haplotype scores... - //ci2 is the haplotype of ci1? - addHaplotype(ci1, orientation1, ci2, p2, ascore, alignment, sameOrientation); - //ci1 is the haplotype of ci2? - //use orientation1 for ci2 as well... (1 - orientation is not really needed) - addHaplotype(ci2, 1 - orientation1, ci1, p1, ascore, revAlignment, sameOrientation); - - for (int orientation2 = 0; orientation2 < 2; ++orientation2) { // four combinations, ++/+-/-+/-- - //add partial haplotype link scores - addChainLink(ci1, orientation1, ci2, orientation2, p1, p2, ascore, alignment, sameOrientation); - //add partial haplotype link scores (1 - orientation is not really needed) - addChainLink(ci2, 1 - orientation2, ci1, 1 - orientation1, p2, p1, ascore, revAlignment, sameOrientation); - //System.err.println("hiphei"); - } - } - } - - ++numAlignments; - } - } else { - if (skip && (row.size() == 3 || row.size() == 1)) - ; - else - System.err.println("Warning: skipping " + row); - } - } while (true); - br.close(); - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - } - System.err.println("loading " + numAlignments + " alignments"); - - System.err.println("Alignment links:"); - for (String key:chainLinkHash.keySet()) - System.err.println(key + "\t" + chainLinkHash.get(key)); - } - - public void addMap(String fn, boolean flip, boolean nochromosome, boolean nointervals, int chromosome) - { - - ArrayList markers = InputData.loadMap(fn, nochromosome, nointervals, compressMap); - - if (flip) { - flip(markers); - } - - for (Marker m : markers) - if (chromosome <= 0 || m.getChromosome() == chromosome) { - boolean inside = false; - if (bedHash.containsKey(m.contig)) - for (ContigInterval ci : bedHash.get(m.getContig())) { - if (ci.inside(m.position)) { - inside = true; - ci.addMarker(m, numMaps); - break; - } - } - if (!inside) { -// System.err.println(m); -// System.err.println("Warning: marker position not listed in the bed file"); - //System.exit(-1); - } - } - System.err.println("#map = " + fn); - System.err.println("contig\tstart_pos\tend_pos\tscore+orientation\tscore-orientation"); - for (ContigInterval ci : bed) { - if (ci.markers.size() > numMaps) { - Collections.sort(ci.markers.get(numMaps)); // Put positions into physical order - - int scores[] = solve(ci.markers.get(numMaps)); // Solve marker positions by dynamic programming... - //ci.scores.set(numMaps, scores); - - System.err.println(ci + "\t" + scores[0] + "\t" + scores[1]); - } - } - ++numMaps; - - int maxChromosome = 0; - for (ContigInterval ci : bed) - maxChromosome = Math.max(maxChromosome, ci.getChromosome()); - - int minChromosome = maxChromosome; - for (ContigInterval ci : bed) - if (ci.getChromosome() > 0) - minChromosome = Math.min(minChromosome, ci.getChromosome()); - - if (minChromosome != maxChromosome) { - System.err.println("Error: Only one chromosome allowed in the input maps!"); - System.exit(-1); - } - - intervalsInChr = new ArrayList(); - - for (ContigInterval ci : bed) { - if (ci.getChromosome() == maxChromosome || ci.getChromosome() == 0) - intervalsInChr.add(ci); - } - - } - - public void combineMaps(boolean findOrientation, boolean randomOrder, boolean finalImprove) - { - //(remove) check intervals without any markers... - ArrayList contigsWithMarkers = new ArrayList(); - - ArrayList contigsWithoutMarkers = new ArrayList(); - - for (ContigInterval ci : intervalsInChr) { - if (ci.markers.size() > 0) - contigsWithMarkers.add(ci); - else { - contigsWithoutMarkers.add(ci); - System.err.println("Contig " + ci + " does not have any markers"); - } - } - intervalsInChr = contigsWithMarkers; - if (keepEmptyIntervals) - for (ContigInterval ci : contigsWithoutMarkers) { - intervalsInChr.add(ci); - } - - - - if (randomOrder) { // destroy initial solution... - Collections.shuffle(intervalsInChr); - for (ContigInterval c : intervalsInChr) - if (Math.random() < 0.5) - c.flipOrientation(); - } else { // find contig order by ordering contigs on the most abundant marker - - ArrayList allMarkers = getMarkers(intervalsInChr, null, 0); - - int maxBin = getMaxBin(allMarkers); - int minBin = getMinBin(allMarkers); - - //int n = intervalsInChr.size(); - ArrayList sortValues = new ArrayList(); - - for (ContigInterval ci : intervalsInChr) { - ArrayList markersInFirstMap = ((ci.markers.size() > 0) ? ci.markers.get(0) : new ArrayList()); - int s[] = solve(markersInFirstMap); - if (s[1] > s[0] || (s[1] == s[0] && Math.random() < 0.5)) - ci.flipOrientation(); - - HashMap hm = new HashMap(); - - for (Marker m : markersInFirstMap) { - int numI = m.intervals.length; - for (int i = 0; i < numI; i+=2) - for (int p = m.intervals[i]; p <= m.intervals[i + 1]; ++p) { - Integer oldValue = hm.get(p); - if (oldValue == null) - hm.put(p, 1); - else - hm.put(p, oldValue + 1); - } - } - - int maxCount = -1; - int bin = 0; - int numMaxCount = 0; - for (int p : hm.keySet()) { - int count = hm.get(p); - if (count > maxCount) { - numMaxCount = 1; - bin = p; - maxCount = count; - } else if (count == maxCount) { - ++numMaxCount; - if (Math.random() * numMaxCount < 1.0) // randomly pick one of multiple bins with maximum count - bin = p; - } - } - if (maxCount > 0) // there was at least one marker - sortValues.add(bin + Math.random()); //bin + rand[0,1] - else // no markers - sortValues.add(Math.random() * (maxBin - minBin + 1) + minBin); - } - Misc.ArrayIndexComparator comparator = new Misc.ArrayIndexComparator(sortValues); - Integer[] indexes = comparator.createIndexArray(); - Arrays.sort(indexes, comparator); - - ArrayList cis_new = new ArrayList(); - for (int i : indexes) - cis_new.add(intervalsInChr.get(i)); - intervalsInChr = cis_new; - } - if (findOrientation && numMaps > 1) { - String orientation = "+"; - String scores = ""; - - //disable chains - HashMap oldChainLinkHash = chainLinkHash; - chainLinkHash = new HashMap(); - - //use only first map - int oldNumMaps = numMaps; - numMaps = 1; - - ArrayList cis_new = new ArrayList(); - - for (ContigInterval ci : intervalsInChr) { - if (ci.markers.size() > 0 && ci.markers.get(0).size() > 0) - cis_new.add(ci); - } - - //Collections.shuffle(cis_new); // random order as init (could be based on most common marker in each contig...) - - for (int map = 1; map < oldNumMaps; ++map) { - int oldNumRuns = numRuns; - if (map == 1) - numRuns = Math.min(2, oldNumRuns); - else - numRuns = 1; // this is probably enough... - improveAnchoring(cis_new, false, true); - numRuns = oldNumRuns; - - numMaps = map + 1; - - int score1 = calculateScore(cis_new, numMaps, false); - - flip(getMarkers(intervalsInChr, null, map)); - - int score2 = calculateScore(cis_new, numMaps, false); - - if (score1 >= score2) { - flip(getMarkers(intervalsInChr, null, map)); - orientation += " +"; - } else - orientation += " -"; - - scores = scores + " " + (score1 - score2); - - - //add new markers to cis_new to random places... - - ArrayList cis_new2 = new ArrayList(); - out: for (ContigInterval ci : intervalsInChr) { - if (ci.markers.size() > map && ci.markers.get(map).size() > 0) { - for (int m = 0; m < map; ++m) - if (ci.markers.get(m).size() > 0) - continue out; - cis_new2.add(ci); - } - } - //Collections.shuffle(cis_new2); no need to shuffle... - cis_new = Misc.randomMerge(cis_new, cis_new2); - } - //System.out.println(intervalsInChr.size() + "\t" + cis_new.size() ); - - //intervalsInChr.clear(); - //intervalsInChr.addAll(cis_new); - - //finally add contigs without markers - for (ContigInterval ci : intervalsInChr) - if (ci.markers.size() == 0) - cis_new.add(ci); - - assert(intervalsInChr.size() == cis_new.size()); - - - intervalsInChr = cis_new; - - //enable chains - chainLinkHash = oldChainLinkHash; - - System.out.println("#found orientation=" + orientation + " ( support " + scores + " )"); - } - if (finalImprove) - improveAnchoring(); - } - - private ArrayList getMarkers(ArrayList cis, ContigInterval excluded, int map) - { - ArrayList ret = new ArrayList(); - for (ContigInterval ci : cis) - if (ci != excluded) - ret.addAll(ci.getMarkers(map)); - return ret; - } - - private String calculateKey(ContigInterval prev, boolean orientationPrev, ContigInterval ci, boolean orientationCi){ - return prev.toString() + ((orientationPrev) ? '+':'-') + ci.toString() + ((orientationCi) ? '+':'-'); - } - - private String calculateKey(ContigInterval prev, ContigInterval ci){ - return calculateKey(prev, (prev.getOrientation() >= 0), ci, (ci.getOrientation() >= 0)); - } - - //chainScore with possible flips for prev and ci - private int calculateChainScore(ContigInterval prev, boolean flipPrev, ContigInterval ci, boolean flipCi) { - String key = calculateKey(prev, (prev.getOrientation() >= 0) ^ flipPrev, ci, (ci.getOrientation() >= 0) ^ flipCi); - return calculateChainScore(key); - } - private int calculateChainScore(ContigInterval prev, ContigInterval ci) { - String key = calculateKey(prev, ci); - return calculateChainScore(key); - } - - private int calculateChainScore(String key) { - if (chainLinkHash.containsKey(key)) { - int ret = chainLinkHash.get(key); - if (useChainAndPaf && scaffoldingLink.containsKey(key)) - ret += scaffoldingLink.get(key); - return ret; - } - if (scaffoldingLink.containsKey(key)) - return scaffoldingLink.get(key); - return 0; - } - - // haplotype is haplotype of ofHaplotype? - private int calculateChainScoreHaplotype(ContigInterval ofHaplotype, ContigInterval haplotype) { - String key = calculateKey(ofHaplotype, haplotype); - key = key.substring(0, key.length() - 1); // last orientation - - if (chainHaplotypeHash.containsKey(key)) - return chainHaplotypeHash.get(key); - return 0; - } - - private long[] calculateChainScoreHaplotypeCut(ContigInterval ofHaplotype, ContigInterval haplotype) { - String key = calculateKey(ofHaplotype, haplotype); - key = key.substring(0, key.length() - 1); // last orientation - - return chainHaplotypeCut.get(key); - } - - private int calculateChainScore(ArrayList cis) - { - int score = 0; - ContigInterval prev = null; - for (ContigInterval ci : cis) { - if (prev != null) - score += calculateChainScore(prev, ci); - prev = ci; - } - return score; - } - - private int calculateProximityScore(ArrayList cis) - { - if (prox != null) - return prox.score(cis); - return 0; - } - - private int evaluateScore(ArrayList eval, boolean improve) - { - ArrayList eval2 = new ArrayList(); - for (ContigInterval e : eval) { - boolean found = false; - for (ContigInterval ci : intervalsInChr) { - if (ci.equals(e)) { - eval2.add(ci); - ci.setOrientation(e.getOrientation()); - found = true; - break; - } - } - if (!found) { - System.err.println("Contig " + e + " not found"); - } - } - - if (improve) { - int score = calculateScore(eval2, false) + calculateNonMarkerScore(eval2); - //System.out.print("#initial score " + score + " "); - improveAnchoring(eval2, true, true); - } else { - ArrayList orderSupport = calculateSupport(eval2); - int score = calculateScore(eval2, false) + calculateNonMarkerScore(eval2); - System.out.println("#final score " + score); - printAnchoring(eval2, orderSupport); - findLikelyAssemblyErrors(eval2); - } - //change bed to the evaluated order... - intervalsInChr.clear(); - intervalsInChr.addAll(eval2); - - return calculateScore(eval2, false) + calculateNonMarkerScore(eval2); -/* - int score1 = calculateScore(eval2, true); - for (int map = 0; map < numMaps; ++map) - flip(getMarkers(intervalsInChr, null, map)); - int score2 = calculateScore(eval2, true); - - return Math.max(score1, score2);*/ - } - - - private int calculateScoreFast(ArrayList cis) - { - int score = 0; - for (int map = 0; map < numMaps; ++map) { - int s = solveForwardFast(getMarkers(cis, null, map)); - score += s; - } - return score; - } - - - private int calculateScore(ArrayList cis, boolean verbose) - { - return calculateScore(cis, numMaps, verbose); - } - - private int calculateScore(ArrayList cis, int numMaps, boolean verbose) - { - int score = 0; - for (int map = 0; map < numMaps; ++map) { - int s = solveForward(getMarkers(cis, null, map)); - if (verbose) - System.err.println("map " + map + " score " + s); - score += s; - } - return score; - } - - private int getMaxBin(ArrayList markers) { - int maxBin = Integer.MIN_VALUE; - for (Marker m : markers) - maxBin = Math.max(maxBin, m.maxBin()); - if (maxBin != Integer.MIN_VALUE) - return maxBin; - else - return 0; - } - - private int getMinBin(ArrayList markers) { - int minBin = Integer.MAX_VALUE; - for (Marker m : markers) - minBin = Math.min(minBin, m.minBin()); - if (minBin != Integer.MAX_VALUE) - return minBin; - else - return 0; - } - - //TODO: Remove calculateChainScore(cis) and calculateChainScore(c1, c2) add calculateNonMarkerScore(...) - private ArrayList calculateSupport(ArrayList cis) { - - //for (int iteration = 0; iteration < 1; ++iteration) { // add here "iteration < 2" if possible orientation errors should be corrected... - - - - //ArrayList score1 = new ArrayList(); - ArrayList score2 = new ArrayList(); - - ArrayList order1 = new ArrayList(); - ArrayList order2 = new ArrayList(); - - int rank = 0; - for (ContigInterval ci : cis) { - ci.setRank(rank++); - score2.add(0); - order1.add(0); - order2.add(0); - } - - for (int map = 0; map < numMaps; ++map) { - ArrayList markers = getMarkers(cis, null, map); - - int endBin = getMaxBin(markers); - int startBin = getMinBin(markers); - - int numMarkers = markers.size(); - int S[][] = new int[numMarkers + 1][endBin - startBin + 1]; - int P[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - int Sb[][] = new int[numMarkers + 1][endBin - startBin + 1]; - //int Pb[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - int Sc[][] = new int[numMarkers + 1][endBin - startBin + 1]; - int Pc[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - //simple way to calculate (about correct) backward table... - Collections.reverse(markers); - forwardFast2(markers, Sb, startBin, endBin); - Collections.reverse(markers); - - forward1(markers, S, P, startBin, endBin); - int maxScore = -1; - { - int maxI = startBin; - for (int i = startBin; i <= endBin; ++i) - if (S[numMarkers][i - startBin] > maxScore) { - maxScore = S[numMarkers][i - startBin]; - maxI = i; - } - - for (int mi = numMarkers; mi > 0; --mi) { - markers.get(mi - 1).pPlus = maxI; - //System.err.println(markers.get(mi - 1) + "\t" + maxI); - maxI = P[mi][maxI - startBin]; - } - } - - ArrayList> markerIndex = new ArrayList>(); - for (ContigInterval ci : cis) - markerIndex.add(new ArrayList()); - - for (int mi = 0; mi < numMarkers; ++mi) { - Marker m = markers.get(mi); - rank = m.ci.getRank(); - markerIndex.get(rank).add(mi); - } - for (ContigInterval ci : cis) { - rank = ci.getRank(); - - ArrayList cMarkers = new ArrayList(); - - for (int i : markerIndex.get(rank)) - cMarkers.add(markers.get(i)); - - int maxB = Integer.MIN_VALUE; - int minB = Integer.MAX_VALUE; - - int numCMarkers = cMarkers.size(); - - if (numCMarkers > 0) { - for (Marker m : cMarkers) { - maxB = Math.max(maxB, m.pPlus); - minB = Math.min(minB, m.pPlus); - } - if (maxB > minB) - order1.set(rank, order1.get(rank) + 1); - - Collections.reverse(cMarkers); - int start = markerIndex.get(rank).get(0); - int end = markerIndex.get(rank).get(numCMarkers - 1) + 1; - - for (int b = startBin; b <= endBin; ++b) { - Sc[0][b - startBin] = S[start][b - startBin]; - Pc[0][b - startBin] = P[start][b - startBin]; - } - - forward1(cMarkers, Sc, Pc, startBin, endBin); - - - int Scc[] = Sc[cMarkers.size()]; - for (int b = startBin + 1; b <= endBin; ++b) { // make table non-decreasing - if (Scc[b - startBin] < Scc[b - startBin - 1]) - Scc[b - startBin] = Scc[b - startBin - 1]; - } - int maxI = startBin; - int max = -1; - for (int b = startBin; b <= endBin; ++b) { - int s = Scc[b - startBin] + Sb[numMarkers - end][b - startBin]; - if (s > max) { - max = s; - maxI = b; - } - } - score2.set(rank, score2.get(rank) + max); - { - for (int mi = numCMarkers; mi > 0; --mi) { - cMarkers.get(mi - 1).pPlus = maxI; - //System.err.println(markers.get(mi - 1) + "\t" + maxI); - maxI = Pc[mi][maxI - startBin]; - } - } - maxB = Integer.MIN_VALUE; - minB = Integer.MAX_VALUE; - for (Marker m : cMarkers) { - maxB = Math.max(maxB, m.pPlus); - minB = Math.min(minB, m.pPlus); - } - if (maxB > minB) - order2.set(rank, order2.get(rank) + 1); - } else { - score2.set(rank, score2.get(rank) + maxScore); - } - } - } - int oldNonMarkerScore = calculateNonMarkerScore(cis); - int oldScore = calculateScore(cis, false) + oldNonMarkerScore; - for (ContigInterval ci : cis) { - rank = ci.getRank(); - int newNonMarkerScore = oldNonMarkerScore + nonMarkerScoreChange(cis, rank, rank, 0, true, 0); - score2.set(rank, score2.get(rank) + newNonMarkerScore); - } - - ArrayList ret = new ArrayList(); - //boolean changes = false; - for (ContigInterval c : cis) { - rank = c.getRank(); - int support = oldScore - score2.get(rank); - int order = 0; - if (support == 0 && order1.get(rank) > order2.get(rank)) { - if (c.getOrientation() >= 0) - order = 1; - else - order = -1; - } - else if (support == 0 && order1.get(rank) < order2.get(rank)) { - if (c.getOrientation() >= 0) - order = -1; - else - order = 1; - } - //if (support < 0) { - // c.flipOrientation(); - // changes = true; - //} - //System.err.println("order_support\t" + c +"\t" + ((support == 0) ? 0.5 * order: support)); - ret.add(((support == 0) ? (0.5 * order): support)); - } - //if (!changes) - // break; - //} - return ret; - } - - //flip(i..cr) - //move(i...cr) to new_pos w/wo flipping - //move cr to new position - - private int calculateNonMarkerScore(ArrayList cis) - { - return calculateChainScore(cis) + calculateProximityScore(cis); - } - - - //start...end => start+moveDirection...end+moveDirection - public static void changeOrder(ArrayList cis, int start, int end, int moveDirection, boolean flip) - { - int n = cis.size(); - if (end + moveDirection >= n || start + moveDirection < 0) { - System.err.println("change too large"); - return; - } - if (moveDirection == 0 && !flip) // nothing to do - return; - - ArrayList cis_new = new ArrayList(); - if (moveDirection <= 0) { - for (int i = 0; i < start + moveDirection; ++i) - cis_new.add(cis.get(i)); - - if (!flip) - for (int i = start; i <= end; ++i) - cis_new.add(cis.get(i)); - else - for (int i = end; i >= start; --i) { //reverse and flip - ContigInterval ci = cis.get(i); - ci.flipOrientation(); - cis_new.add(ci); - } - - for (int i = start + moveDirection; i < start; ++i) - cis_new.add(cis.get(i)); - for (int i = end + 1; i < n; ++i) - cis_new.add(cis.get(i)); - } else { - for (int i = 0; i < start; ++i) - cis_new.add(cis.get(i)); - - for (int i = 1; i <= moveDirection; ++i) - cis_new.add(cis.get(i + end)); - - if (!flip) - for (int i = start; i <= end; ++i) - cis_new.add(cis.get(i)); - else - for (int i = end; i >= start; --i) { //reverse and flip - ContigInterval ci = cis.get(i); - ci.flipOrientation(); - cis_new.add(ci); - } - - for (int i = end + moveDirection + 1; i < n; ++i) - cis_new.add(cis.get(i)); - } - cis.clear(); - cis.addAll(cis_new); - } - - //reverse of changeOrder - public static void changeOrderReverse(ArrayList cis, int start, int end, int moveDirection, boolean flip) - { - changeOrder(cis, start + moveDirection, end + moveDirection, -moveDirection, flip); - } - public int nonMarkerScoreChange(ArrayList cis, int start, int end, int moveDirection, boolean flip, int flipScore) - { - int ret = ((flip) ? flipScore : 0); - if (moveDirection == 0) { // flip - if (flip) { - if (start > 0) { - ret -= calculateChainScore(cis.get(start - 1), cis.get(start)); - ret += calculateChainScore(cis.get(start - 1), false, cis.get(end), true); - } - if (end + 1 < cis.size()) { - ret -= calculateChainScore(cis.get(end), cis.get(end + 1)); - ret += calculateChainScore(cis.get(start), true, cis.get(end + 1), false); - } - } // else nothing to do... - } else { // move one or multiple contigs... - ContigInterval sc = cis.get(start); - ContigInterval ec = cis.get(end); - - if (start > 0) { - ret -= calculateChainScore(cis.get(start - 1), sc); - if (end + 1 < cis.size()) - ret += calculateChainScore(cis.get(start - 1), cis.get(end + 1)); - } - if (end + 1 < cis.size()) - ret -= calculateChainScore(ec, cis.get(end + 1)); - - if (moveDirection <= 0) { - if (flip) { - if (start + moveDirection > 0) { - ret -= calculateChainScore(cis.get(start + moveDirection - 1), cis.get(start + moveDirection)); - ret += calculateChainScore(cis.get(start + moveDirection - 1), false, ec, true); - } - ret += calculateChainScore(sc, true, cis.get(start + moveDirection), false); - } else { - if (start + moveDirection > 0) { - ret -= calculateChainScore(cis.get(start + moveDirection - 1), cis.get(start + moveDirection)); - ret += calculateChainScore(cis.get(start + moveDirection - 1), sc); - } - ret += calculateChainScore(ec, cis.get(start + moveDirection)); - } - } else { // moveDirection > 0 - if (flip) { - if (end + moveDirection + 1 < cis.size()) { - ret -= calculateChainScore(cis.get(end + moveDirection), cis.get(end + moveDirection + 1)); - ret += calculateChainScore(sc, true, cis.get(end + moveDirection + 1), false); - } - ret += calculateChainScore(cis.get(end + moveDirection), false, ec, true); - } else { - if (end + moveDirection + 1 < cis.size()) { - ret -= calculateChainScore(cis.get(end + moveDirection), cis.get(end + moveDirection + 1)); - ret += calculateChainScore(ec, cis.get(end + moveDirection + 1)); - } - ret += calculateChainScore(cis.get(end + moveDirection), sc); - } - } - } - if (prox != null) - ret += prox.scoreChange(cis, start, end, moveDirection, flip); - return ret; - } - - private boolean linkedIntervals(ArrayList cis, int c1, int c2) - { - return (calculateChainScore(cis.get(c1), cis.get(c2)) > 0 || (prox != null && prox.linkedIntervals(cis, c1, c2))); - } - - //join contig to linked contigs next to it and try to improve... - //not thread safe... fix?: separate scaffoldingLink initialisation - private int calculateBestLinkedContig(ArrayList cis, int cr, boolean verbose){ - - int start = cr; - if (start > 0 && linkedIntervals(cis, start - 1, start)) - return 0; // only start from the first contig of each linked chain of contigs - - int numCis = cis.size(); - - int end = cr; - while (end + 1 < numCis && linkedIntervals(cis, end, end + 1)) - ++end; - - boolean mapMarkers = false; - for (int i = start; i <= end; ++i) - if (cis.get(i).markers.size() > 0) { - mapMarkers = true; - break; - } - if (!mapMarkers) // no need to go further without markers... - return 0; - - int numLinked = end - start + 1; - - if (start < cr || start == end || numLinked == numCis) // starts at c, at least two ContigIntervals and not connected to all - return 0; - - ContigInterval ci_new = new ContigInterval("\t", 1, 1); // dummy name, this cannot be loaded from a file... - - int flipScore = 0; // calculate flipsScore to take into account possible decreasing score when flipping start..end - for (int i = start; i < end; ++i) - flipScore += calculateChainScore(cis.get(i + 1), true, cis.get(i), true) - calculateChainScore(cis.get(i), cis.get(i + 1)); - - //add scaffolding links, use scaffoldingLink_tmp not to mess other links... - scaffoldingLink_tmp.clear(); - for (int i = -1; i < numCis; ++i) - if (i < start || i > end) { - int moveDirection = ((i < start) ? i - start + 1 : i - end); - int nmss = nonMarkerScoreChange(cis, start, end, moveDirection, false, flipScore); - int nmss2 = nonMarkerScoreChange(cis, start, end, moveDirection, true, flipScore); - if (i >= 0) { - ContigInterval ci = cis.get(i); - if (moveDirection != 0) // with links for moveDirection==0, we end up updating start-1...end as well - scaffoldingLink_tmp.put(calculateKey(ci, ci_new), nmss); - scaffoldingLink_tmp.put(calculateKey(ci, ci.getOrientation() >= 0, ci_new, false), nmss2); - } else { // move to the very first position... - int firstMarker = ((start > 0) ? 0 : end + 1); - ContigInterval ci = cis.get(firstMarker); - if (moveDirection != 0) // with links for moveDirection==0, we end up updating start...end+1 as well - scaffoldingLink_tmp.put(calculateKey(ci_new, ci), nmss); - scaffoldingLink_tmp.put(calculateKey(ci_new, false, ci, ci.getOrientation() >= 0), nmss2); - } - } - - //and markers - for (int map = 0; map < numMaps; ++map) { - for (int i = start; i <= end; ++i) { - for (Marker m : cis.get(i).getMarkers(map)) - ci_new.addMarker(new Marker(m), map); - } - } - - ArrayList cis_new = new ArrayList(); - for (int i = 0; i < start; ++i) - cis_new.add(cis.get(i)); - cis_new.add(ci_new); - for (int i = end + 1; i < numCis; ++i) - cis_new.add(cis.get(i)); - - //int os = this.calculateScore(cis, false) + calculateNonMarkerScore(cis); - //int ns2 = this.calculateScore(cis_new, false) + calculateNonMarkerScore(cis_new); - //int ns = this.calculateScore(cis_new, false) + calculateNonMarkerScore(cis_new); - //System.err.println("linked " + ret + " " + start + "," + end + " " +ns2 + " " + os + " " + ns); - - Proximity tmpProx = prox; - prox = null; // do not use proximity - HashMap tmpScaffoldingLink = scaffoldingLink; - scaffoldingLink = scaffoldingLink_tmp; - HashMap tmpChainLink = chainLinkHash; - chainLinkHash = new HashMap(); //nor chainLinks - - int ret = calculateBest(cis_new, ci_new, true); - - prox = tmpProx; - scaffoldingLink = tmpScaffoldingLink; - chainLinkHash = tmpChainLink; - - if (ret > 0) { // update cis - //System.err.println("linked improvement " + ret); - int crn = 0; - for (ContigInterval ci : cis_new) - if (ci == ci_new) - break; - else - ++crn; - - cis_new.clear(); - //int os = calculateScore(cis, false) + calculateNonMarkerScore(cis); - - for (int i = 0; i < crn; ++i) - if (i >= start) - cis_new.add(cis.get(i + numLinked)); - else - cis_new.add(cis.get(i)); - int orientation = ci_new.getOrientation(); - if (orientation >= 0) - for (int i = start; i <= end; ++i) - cis_new.add(cis.get(i)); - else - for (int i = end; i >= start; --i) { - cis.get(i).flipOrientation(); - cis_new.add(cis.get(i)); - } - for (int i = crn; i < numCis - numLinked; ++i) - if (i >= start) - cis_new.add(cis.get(i + numLinked)); - else - cis_new.add(cis.get(i)); - int ns = calculateScore(cis_new, false) + calculateNonMarkerScore(cis_new); - //if (os >= ns) { - // if (orientation < 0) - // for (int i = start; i <= end; ++i) - // cis.get(i).flipOrientation(); - // System.err.println("Error: Score not improving! " + start + "-" + end + "\t" + crn + "\t" + orientation); - // return 0; - //} - System.err.println("LINKED\t" + cis.get(start) + "\t" + (end - start + 1) + "\t" + ns); - cis.clear(); - cis.addAll(cis_new); - } - return ret; - } - //join contig to linked contigs next to it and try to improve... - //thread safe version... - private int calculateBestLinkedContig_ts(ArrayList cis, int cr, boolean verbose){ - - int start = cr; - if (start > 0 && linkedIntervals(cis, start - 1, start)) - return 0; // only start from the first contig of each linked chain of contigs - - int numCis = cis.size(); - - int end = cr; - while (end + 1 < numCis && linkedIntervals(cis, end, end + 1)) - ++end; - - int numLinked = end - start + 1; - - if (start < cr || start == end || numLinked == numCis) // starts at c, at least two ContigIntervals and not connected to all - return 0; - - boolean mapMarkers = false; - for (int i = start; i <= end; ++i) - if (cis.get(i).markers.size() > 0) { - mapMarkers = true; - break; - } - - if (!mapMarkers) // no need to go further without markers... - return 0; - - //update rank - int rank = 0; - for (ContigInterval ci : cis) - ci.setRank(rank++); - - int bestRes[] = new int[numCis + 1]; - int bestRes2[] = new int[numCis + 1]; - - for (int map = 0; map < numMaps; ++map) { - - ArrayList cMarkers = new ArrayList(); - for (int i = start; i <= end; ++i) - cMarkers.addAll(cis.get(i).getMarkers(map)); - - ArrayList markers = new ArrayList(); - for (int i = 0; i < start; ++i) - markers.addAll(cis.get(i).getMarkers(map)); - for (int i = end + 1; i < numCis; ++i) - markers.addAll(cis.get(i).getMarkers(map)); - - int endBin = getMaxBin(markers); - int startBin = getMinBin(markers); - - for (Marker m : cMarkers) { - endBin = Math.max(endBin, m.maxBin()); - startBin = Math.min(startBin, m.minBin()); - } - //System.err.println(startBin); - //System.err.println(endBin); - - int numMarkers = markers.size(); - int S[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - int Sb[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - int Sc[][] = new int[1 + cMarkers.size()][endBin - startBin + 1]; - - forwardFast1(markers, S, startBin, endBin); - //simple way to calculate (about correct) backward table... - Collections.reverse(markers); - forwardFast2(markers, Sb, startBin, endBin); - Collections.reverse(markers); - - int old = -1; - for (int mi = 0; mi <= numMarkers; ++mi) { - - if (mi < numMarkers) { - Marker m = markers.get(mi); // this is the next marker - numCis = m.ci.getRank(); - } else - numCis = cis.size(); - - if (numCis != old) { - for (int b = 0; b <= endBin - startBin; ++b) - Sc[0][b] = S[mi][b]; - - forwardFast1(cMarkers, Sc, startBin, endBin); - - int Scc[] = Sc[cMarkers.size()]; - for (int b = 1; b <= endBin - startBin; ++b) { // make table non-decreasing - if (Scc[b] < Scc[b - 1]) - Scc[b] = Scc[b - 1]; - } - int Sbb[] = Sb[numMarkers - mi]; - int max = -1; - for (int b = 0; b <= endBin - startBin; ++b) { - int s = Scc[b] + Sbb[b]; - if (s > max) - max = s; - } - //System.out.println(max); - for (int i = old + 1; i <= numCis; ++i) - bestRes[i] += max; - - Collections.reverse(cMarkers); - forwardFast1(cMarkers, Sc, startBin, endBin); - Collections.reverse(cMarkers); - - for (int b = 1; b <= endBin - startBin; ++b) { // make table non-decreasing - if (Scc[b] < Scc[b - 1]) - Scc[b] = Scc[b - 1]; - } - max = -1; - for (int b = 0; b <= endBin - startBin; ++b) { - int s = Scc[b] + Sbb[b]; - if (s > max) - max = s; - } - //System.out.println(max); - for (int i = old + 1; i <= numCis; ++i) - bestRes2[i] += max; - } - old = numCis; - } - } - - numCis = cis.size(); - int flipScore = 0; // calculate flipsScore to take into account possible decreasing score when flipping start..end - for (int i = start; i < end; ++i) - flipScore += calculateChainScore(cis.get(i + 1), true, cis.get(i), true) - calculateChainScore(cis.get(i), cis.get(i + 1)); - - int oldNonMarkerScore = calculateNonMarkerScore(cis); - int oldScore = calculateScore(cis, false) + oldNonMarkerScore; - for (int ci = 0; ci <= numCis; ++ci) { - int mD = ci - start; - if (ci > start) - if (ci > end) - mD = ci - end - 1; - else - mD = 0; - bestRes[ci] += oldNonMarkerScore + nonMarkerScoreChange(cis, start, end, mD, false, flipScore); - bestRes2[ci]+= oldNonMarkerScore + nonMarkerScoreChange(cis, start, end, mD, true, flipScore); - } - //for (int ci = 0; ci <= cis.size(); ++ci) { - // System.err.print(bestRes[ci] + " "); - //} - //System.err.println(); - - int max = 0; - for (int ci = 0; ci <= cis.size(); ++ci) - if (bestRes[ci] > bestRes[max]) - max = ci; - - int max2 = 0; - for (int ci = 0; ci <= cis.size(); ++ci) - if (bestRes2[ci] > bestRes2[max2]) - max2 = ci; - - if (bestRes[max] > oldScore || bestRes2[max2] > oldScore) { - int pos = max2; - boolean flip = true; - if (bestRes[max] >= bestRes2[max2]) { - flip = false; - pos = max; - if (verbose) - System.err.println("LINKED_MOVE\t" + cis.get(start) + "\t" + numLinked + "\t" + bestRes[max]);// + "\t" + oldScore); - - } else { - if (verbose) - System.err.println(((max2 == start || max2 == start + 1) ? "LINKED_FLIP\t":"LINKED_MOVE+FLIP\t") + cis.get(start) + "\t" + numLinked + "\t" + bestRes2[max2]);// + "\t" + oldScore); - } - - //table index is the new rank for the contig start... without flipping... - int mD = pos - start; - if (pos > start) - if (pos > end) - mD = pos - end - 1; - else - mD = 0; - changeOrder(cis, start, end, mD, flip); - } - //int ns = (calculateNonMarkerScore(cis) + calculateScore(cis, false)); - //int ns2 = Math.max(bestRes[max], bestRes2[max2]); - //if (ns != ns2) - // System.err.println("LINKED_DIFF " + oldScore + " " + ns + " " + ns2); - return Math.max(bestRes[max], bestRes2[max2]) - oldScore; - } - - - private int calculateBest(ArrayList cis, ContigInterval c, boolean verbose) { - - //update rank - int numCis = 0; - for (ContigInterval ci : cis) - ci.setRank(numCis++); - - boolean mapMarkers = (c.markers.size() > 0); - - int oldNonMarkerScore = calculateNonMarkerScore(cis); - int oldScore = oldNonMarkerScore + ((mapMarkers) ? calculateScore(cis, false) : 0); - - int cr = c.getRank(); - - //try flipping and/or moving multiple Contigs [i...cr] - if (cr > 0) { - int minFlipStart = cr - 1; - while (minFlipStart >= 0 && linkedIntervals(cis, minFlipStart, minFlipStart + 1)) { - --minFlipStart; - } - - int flipS = 0; //calculate this to fix the nonsymmetrical pair-wise scores... - for (int flipStart = cr - 1; flipStart > minFlipStart; --flipStart) { - - if (!mapMarkers && cis.get(flipStart).markers.size() > 0) { - mapMarkers = true; - oldScore += calculateScore(cis, false); - } - - flipS -= calculateChainScore(cis.get(flipStart), cis.get(flipStart + 1)); //handles the nonsymmetrical pair-wise scores... - flipS += calculateChainScore(cis.get(flipStart + 1), true, cis.get(flipStart), true); //handles the nonsymmetrical pair-wise scores... - - int flipScore = nonMarkerScoreChange(cis, flipStart, cr, 0, true, flipS); - - if (flipScore >= 0) { //chainScore must not get worse, now calculate the marker score as well - - changeOrder(cis, flipStart, cr, 0, true); - int nms = calculateNonMarkerScore(cis); - if (nms - oldNonMarkerScore != flipScore) { - System.err.println("Error:scores differ " + (nms - oldNonMarkerScore) + " " + flipScore); - } - int newScore = ((mapMarkers) ? calculateScoreFast(cis) + nms : nms); - if (newScore > oldScore) {// and the total score improves - if (verbose) - System.err.println("FLIP\t" + c + "\t" + (cr - flipStart + 1) + "\t" + (newScore)); - return newScore - oldScore; - } else - changeOrderReverse(cis, flipStart, cr, 0, true); - } - - //try moving multiple Contigs [i...cr] with or without flipping... - out: for (int direction = -1; direction < 2;direction+=2){ - - ArrayList scores = new ArrayList(); - for (int movePosition = direction; cr + movePosition < numCis && flipStart + movePosition >= 0; movePosition+=direction) { - int moveScore1 = nonMarkerScoreChange(cis, flipStart, cr, movePosition, false, 0); - int moveScore2 = nonMarkerScoreChange(cis, flipStart, cr, movePosition, true, flipS); - scores.add(-Math.max(moveScore1, moveScore2)); - } - //sort possible positions based on the chain (nonMarker) score... - Misc.ArrayIndexComparator comparator = new Misc.ArrayIndexComparator(scores); - Integer[] indexes = comparator.createIndexArray(); - Arrays.sort(indexes, comparator); - - int maxEvaluations = 4; - - for (int i : indexes) { - int movePosition = (i + 1) * direction; - int moveScore1 = nonMarkerScoreChange(cis, flipStart, cr, movePosition, false, 0); - if (moveScore1 >= 0) { - changeOrder(cis, flipStart, cr, movePosition, false); - int score = ((mapMarkers) ? calculateScoreFast(cis) + calculateNonMarkerScore(cis) : calculateNonMarkerScore(cis)); - if (score > oldScore) { - if (verbose) - System.err.println("MOVE\t" + c + "\t" + (cr - flipStart + 1) + "\t" + score); - return score - oldScore; - } else - changeOrderReverse(cis, flipStart, cr, movePosition, false); - } - int moveScore2 = nonMarkerScoreChange(cis, flipStart, cr, movePosition, true, flipS); - if (moveScore2 >= 0) { - changeOrder(cis, flipStart, cr, movePosition, true); - int score = ((mapMarkers) ? calculateScoreFast(cis) + calculateNonMarkerScore(cis) : calculateNonMarkerScore(cis)); - if (score > oldScore) { - if (verbose) - System.err.println("MOVE+FLIP\t" + c + "\t" + (cr - flipStart + 1) + "\t" + score); - return score - oldScore; - } else - changeOrderReverse(cis, flipStart, cr, movePosition, true); - } - //if (--maxEvaluations <= 0 || (moveScore1 < 0 && moveScore2 < 0)) // maximum number of evaluations reached... - if (--maxEvaluations <= 0) // maximum number of evaluations reached... - continue out; - } - } - } - } - //Move only contig c into new position - int bestRes[] = new int[cis.size() + 1]; - int bestRes2[] = new int[cis.size() + 1]; - - //System.err.println(oldScore + "\t" + oldChainScore); - if (c.markers.size() > 0) { // no need without markers in c... - for (int map = 0; map < numMaps; ++map) { - ArrayList markers = getMarkers(cis, c, map); - ArrayList cMarkers = c.getMarkers(map); - - int endBin = getMaxBin(markers); - int startBin = getMinBin(markers); - - for (Marker m : cMarkers) { - endBin = Math.max(endBin, m.maxBin()); - startBin = Math.min(startBin, m.minBin()); - } - //System.err.println(startBin); - //System.err.println(endBin); - - int numMarkers = markers.size(); - int S[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - int Sb[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - int Sc[][] = new int[1 + cMarkers.size()][endBin - startBin + 1]; - - forwardFast1(markers, S, startBin, endBin); - //simple way to calculate (about correct) backward table... - Collections.reverse(markers); - forwardFast2(markers, Sb, startBin, endBin); - Collections.reverse(markers); - - int old = -1; - for (int mi = 0; mi <= numMarkers; ++mi) { - - if (mi < numMarkers) { - Marker m = markers.get(mi); // this is the next marker - numCis = m.ci.getRank(); - } else - numCis = cis.size(); - - if (numCis != old) { - for (int b = 0; b <= endBin - startBin; ++b) - Sc[0][b] = S[mi][b]; - - forwardFast1(cMarkers, Sc, startBin, endBin); - - int Scc[] = Sc[cMarkers.size()]; - for (int b = 1; b <= endBin - startBin; ++b) { // make table non-decreasing - if (Scc[b] < Scc[b - 1]) - Scc[b] = Scc[b - 1]; - } - int Sbb[] = Sb[numMarkers - mi]; - int max = -1; - for (int b = 0; b <= endBin - startBin; ++b) { - int s = Scc[b] + Sbb[b]; - if (s > max) - max = s; - } - //System.out.println(max); - for (int i = old + 1; i <= numCis; ++i) - bestRes[i] += max; - - Collections.reverse(cMarkers); - forwardFast1(cMarkers, Sc, startBin, endBin); - Collections.reverse(cMarkers); - - for (int b = 1; b <= endBin - startBin; ++b) { // make table non-decreasing - if (Scc[b] < Scc[b - 1]) - Scc[b] = Scc[b - 1]; - } - max = -1; - for (int b = 0; b <= endBin - startBin; ++b) { - int s = Scc[b] + Sbb[b]; - if (s > max) - max = s; - } - //System.out.println(max); - for (int i = old + 1; i <= numCis; ++i) - bestRes2[i] += max; - } - old = numCis; - } - } - } else { - if (mapMarkers) // if there are no markers in c, then we can only consider nonMarkerScore after this... - oldScore = oldNonMarkerScore; - mapMarkers = false; - } - for (int ci = 0; ci <= cis.size(); ++ci) { - int mD = ci - cr; - if (ci > cr) - --mD; - bestRes[ci] += oldNonMarkerScore + nonMarkerScoreChange(cis, cr, cr, mD, false, 0); - bestRes2[ci]+= oldNonMarkerScore + nonMarkerScoreChange(cis, cr, cr, mD, true, 0); - } - //for (int ci = 0; ci <= cis.size(); ++ci) { - // System.err.print(bestRes[ci] + " "); - //} - //System.err.println(); - - int max = 0; - for (int ci = 0; ci <= cis.size(); ++ci) - if (bestRes[ci] > bestRes[max]) - max = ci; - - int max2 = 0; - for (int ci = 0; ci <= cis.size(); ++ci) - if (bestRes2[ci] > bestRes2[max2]) - max2 = ci; - - if (bestRes[max] > oldScore || bestRes2[max2] > oldScore) { - int pos = max2; - if (bestRes[max] >= bestRes2[max2]) { - pos = max; - if (verbose) - System.err.println("MOVE\t" + c + "\t1\t" + bestRes[max]); - } else { - if (verbose) - System.err.println(((max2 == cr || max2 == cr + 1) ? "FLIP\t":"MOVE+FLIP\t") + c + "\t1\t" + bestRes2[max2]); - c.flipOrientation(); - } - cis.remove(cr); - if (pos > cr) - cis.add(pos - 1, c); - else - cis.add(pos, c); - } - return Math.max(bestRes[max], bestRes2[max2]) - oldScore; - } - - private ArrayList cloneContigs(ArrayList cis) - { - ArrayList ret = new ArrayList(); - for (ContigInterval c : cis) - ret.add(new ContigInterval(c)); - return ret; - } - - private void improveAnchoring() - { - improveAnchoring(intervalsInChr, true, true); - } - - //parallel implementation of calculateBest - private class CalculateBest implements Runnable{ - private AtomicInteger index; - private AtomicBoolean stop; - private ArrayList cis; - private ArrayList cisOrig; - private ArrayList perm; - private boolean verbose; - private boolean linked; - - private CalculateBest next; - private Thread thread; - - private int score = 0; - private int scoreIndex = 0; - CalculateBest(ArrayList cis, ArrayList perm, AtomicInteger index, AtomicBoolean stop, boolean verbose, boolean linked) { - this.index = index; - this.stop = stop; - //clone cis - this.cis = cloneContigs(cis); - this.cisOrig = new ArrayList(); - cisOrig.addAll(this.cis); - this.perm = perm; - this.verbose = verbose; - this.linked = linked; - } - public void setNext(CalculateBest next) - { - this.next = next; - } - public void setScore(int score) - { - this.score = score; - } - @Override - public void run() { - long time = System.currentTimeMillis(); - score = 0; - thread = null; - while (!stop.get()) { - if (thread == null && next != null && System.currentTimeMillis() - time >= 1000) { // start a new thread every 1 secs - next.setCis(this.cis); - if (stop.get()) //it is worth breaking here - break; - thread = new Thread(next); - if (stop.get()) //and maybe here as well even when we don't use thread... - break; - thread.start(); - } - int i = index.getAndIncrement(); - if (i >= perm.size()) - break; - if (linked) - score = calculateBestLinkedContig_ts(cis, perm.get(i), verbose); // TODO: fix perm to cisOrig order - else - score = calculateBest(cis, cisOrig.get(perm.get(i)), verbose); - if (score > 0) { - scoreIndex = i; - stop.set(true); - } - } - if (thread != null && thread.isAlive()) - try { - thread.join(); - } catch (Exception e) { - e.printStackTrace(); - System.exit(-1); - } - } - public void directRun(int i) { - if (linked) - score = calculateBestLinkedContig_ts(cis, perm.get(i), verbose); - else - score = calculateBest(cis, cisOrig.get(perm.get(i)), verbose); - } - public void setCis(ArrayList cis) - { - // avoid clone - //this.cis = cloneContigs(cis); - makeCisIdentical(this.cis, cis); - } - public int getScore() - { - return score; - } - public int getScoreIndex() - { - return scoreIndex; - } - } - // make two lists of ContigIntervals indentical by placement and orientation, source=>target - // lists must contain exactly the same contigs... - private void makeCisIdentical(ArrayList target, ArrayList source) - { - HashMap map1to1 = new HashMap(); - for (ContigInterval c : target) { - if (map1to1.containsKey(c)) { - System.err.println("Error: same contig multiple times..."); - - for (ContigInterval c2 : target) { - System.err.println(c2); - - } - System.exit(-1); - - } - map1to1.put(c, c); - } - target.clear(); - for (ContigInterval c : source) { - ContigInterval c2 = map1to1.get(c); - if (c2 == null) { - System.err.println("Error: some contig(s) not found..."); - System.exit(-1); - } - target.add(c2); - c2.setOrientation(c.getOrientation()); - } - } - - private void improveAnchoring(ArrayList cis, boolean finalRound, boolean verbose) - { - if (cis.size() == 0) - return; - - if (finalRound) - System.out.print("#initial score " + (calculateScore(cis, false) + calculateNonMarkerScore(cis))); - - - int numCis = cis.size(); - - ArrayList perm = new ArrayList(); - for (int i = 0; i < numCis; ++i) - perm.add(i); - - boolean foundBetter = false; - - - int iteration = 0; - - int bestScore = -1; - ArrayList cis_best = new ArrayList(); - ArrayList cis_bestO = new ArrayList(); - - for (int run = 0; run < numRuns; ++run) { - if (run > 0) { - int n = cis.size(); - for (ContigInterval ci : cis) - if (Math.random() < 0.05) - ci.flipOrientation(); - if (n > 1) - for (int flips = 0; flips < n; ++flips) { - int i = (int) (Math.random() * (n - 1)); - ContigInterval tmp = cis.get(i); - cis.set(i, cis.get(i + 1)); - cis.set(i + 1, tmp); - } - } - - do { - if (finalRound) { - System.err.println("iteration " + (++iteration) + " score=" + (calculateScore(cis, false) + calculateNonMarkerScore(cis))); - } - foundBetter = false; - Collections.shuffle(perm); // random order - - if (numThreads <= 1) { - ArrayList cisOrig = new ArrayList(); - cisOrig.addAll(cis); - for (int i = 0; i < numCis; ++i) - if (calculateBest(cis, cisOrig.get(perm.get(i)), verbose) > 0) - foundBetter = true; - - //try linked version... - if (!foundBetter) - for (int i = 0; i < numCis; ++i) { - //ArrayList cis_ = new ArrayList(); - //cis_.addAll(cis); - //calculateBestLinkedContig_ts(cis_, perm.get(i), verbose); - - if (calculateBestLinkedContig_ts(cis, perm.get(i), verbose) > 0) - foundBetter = true; - } - } else { - for (int linked = 0; linked < 2; ++linked) { - AtomicInteger index = new AtomicInteger(); - AtomicBoolean stop = new AtomicBoolean(); - - CalculateBest[] cbs = new CalculateBest[numThreads]; - for (int t = 0; t < numThreads; ++t) { - cbs[t] = new CalculateBest(cis, perm, index, stop, verbose, linked == 1); - if (t > 0) - cbs[t - 1].setNext(cbs[t]); - } - while (true) { - cbs[0].run(); - int best = 0; - for (int t = 1; t < numThreads; ++t) { - if (cbs[t].getScore() > cbs[best].getScore()) - best = t; - } - if (cbs[best].getScore() > 0) { - if (best > 0) { - CalculateBest tmp = cbs[0]; //swap cbs[0] and cbs[best] - cbs[0] = cbs[best]; - cbs[best] = tmp; - for (int t = 1; t < numThreads; ++t) - cbs[t - 1].setNext(cbs[t]); - cbs[numThreads - 1].setNext(null); - } - for (int t = 1; t < numThreads; ++t) { //run other contigs that improved the score... - if (cbs[t].getScore() > 0) { - cbs[0].directRun(cbs[t].getScoreIndex()); - } - } - stop.set(false); - foundBetter = true; - for (int t = 0; t < numThreads; ++t) - cbs[t].setScore(0); - } else - break; - } - if (foundBetter) { - makeCisIdentical(cis, cbs[0].cis); - break; // linked only if !foundBetter - } - } - //if (!foundBetter) //calculateBestLinkedContig is not thread safe... - // for (int i = 0; i < numCis; ++i) - // if (calculateBestLinkedContig(cis, perm.get(i), verbose) > 0) - // foundBetter = true; - - } - } while (foundBetter); - int score = (calculateScore(cis, false) + calculateNonMarkerScore(cis)); - if (score > bestScore) { - bestScore = score; - cis_best.clear(); - cis_bestO.clear(); - for (ContigInterval ci : cis) { - cis_best.add(ci); - cis_bestO.add(ci.getOrientation()); - } - } else { - cis.clear(); - int i = 0; - for (ContigInterval ci : cis_best) { - cis.add(ci); - ci.setOrientation(cis_bestO.get(i++)); - } - } - } - - if (finalRound) { - ArrayList orderSupport = calculateSupport(cis); - System.out.println(" final score " + (calculateScore(cis, false) + calculateNonMarkerScore(cis))); - printAnchoring(cis, orderSupport); - findLikelyAssemblyErrors(cis); - } - } - - private void printAnchoring(ArrayList cis, ArrayList orderSupport) { - printAnchoring(System.out, cis, orderSupport); - } - private void printAnchoring(PrintStream stream, ArrayList cis, ArrayList orderSupport) { - stream.print("#contig\tstart\tend\torientation\tchr\torder_support\torig_start/alt_start\torig_end/alt_end\tlinked1\tvia_1\tlink_score1\tlinked2\tvia_2\tlink_score2\tpos_support"); - for (int map = 0; map < numMaps; ++map) - stream.print("\tmap" + (map + 1)); - stream.println(); - - boolean knownOrientation[] = new boolean[cis.size()]; // calculate whether global orientation is known... - - - //calculate map info and follow linked2 links (fill forwards in knownOrientation) - int prevC = 0; - for (int cii = 0; cii < cis.size(); ++cii) { - ContigInterval ci = cis.get(cii); - boolean linked2 = false; // from chain file... - if (cii + 1 < cis.size() && linkedIntervals(cis, cii, cii + 1)) - linked2 = true; - if (!linked2) { - for (int map = 0; map < numMaps; ++map) { - int max = Integer.MIN_VALUE; - int min = Integer.MAX_VALUE; - for (int c = prevC; c <= cii; ++c) - for (Marker m : cis.get(c).getMarkers(map)) { - max = Math.max(max, m.pPlus); - min = Math.min(min, m.pPlus); - } - if (max > min) - for (int c = prevC; c <= cii; ++c) - knownOrientation[c] = true; - } - prevC = cii + 1; - } - } - - long starts[] = new long[cis.size()]; // calculate start of each contig - long ends[] = new long[cis.size()]; // calculate end of each contig - - long starts2[] = new long[cis.size()]; // calculate start of each contig - long ends2[] = new long[cis.size()]; // calculate end of each contig - - boolean prevSwapped = false; // fix for contigs cut out more than their length (a haplotype of two other contigs) - for (int cii = 0; cii < cis.size(); ++cii) { - ContigInterval ci = cis.get(cii); - - boolean linked1 = false; // from chain file... - boolean linked2 = false; // from chain file... - if (cii > 0 && linkedIntervals(cis, cii - 1, cii)) - linked1 = true; - if (cii < cis.size() - 1 && linkedIntervals(cis, cii, cii + 1)) - linked2 = true; - - ContigInterval prev = ((linked1) ? cis.get(cii - 1): null); - ContigInterval next = ((linked2) ? cis.get(cii + 1): null); - - long start = ci.getStart(); - long end = ci.getEnd(); - - long start2 = ci.getStart(); - long end2 = ci.getEnd(); - - if (prev != null) { - String key = this.calculateKey(prev, ci); - if (chainLinkHash.containsKey(key)) { - if (ci.getOrientation() >= 0) { - start = chainLinkHashCut2.get(key); - start2 = chainLinkHashCut2a.get(key); - if (prevSwapped) { - long tmp = start; - start = start2; - start2 = tmp; - } - } else { - end = chainLinkHashCut2.get(key); - end2 = chainLinkHashCut2a.get(key); - if (prevSwapped) { - long tmp = end; - end = end2; - end2 = tmp; - } - } - } - } - if (next != null) { - String key = this.calculateKey(ci, next); - if (chainLinkHash.containsKey(key)) { - if (ci.getOrientation() >= 0) { - end = chainLinkHashCut1.get(key); - end2 = chainLinkHashCut1a.get(key); - if (end < start - 1 && end2 > end) { // more than full length aligned... - long tmp = end; - end = end2; - end2 = tmp; - prevSwapped = true; - } else - prevSwapped = false; - } else { - start = chainLinkHashCut1.get(key); - start2 = chainLinkHashCut1a.get(key); - if (end < start - 1 && start2 < start) { // more than full length aligned... - long tmp = start; - start = start2; - start2 = tmp; - prevSwapped = true; - } else - prevSwapped = false; - } - } else - prevSwapped = false; - } - - starts[cii] = start; - starts2[cii] = start2; - - ends[cii] = end; - ends2[cii] = end2; - } - - //finally try skipping negative length contigs and try to find new starts and ends... - for (int cii = 2; cii < cis.size(); ++cii) { - if (ends[cii] >= starts[cii] - 1) { // non negative length - int prevNN = cii - 1; - while (prevNN >= 0 && ends[prevNN] < starts[prevNN] - 1) // negative length - --prevNN; - if (prevNN < cii - 1 && prevNN >= 0 && linkedIntervals(cis, prevNN, cii)) { - ContigInterval ci = cis.get(cii); - ContigInterval ci2 = cis.get(prevNN); - String key = calculateKey(ci2, ci); - if (chainLinkHash.containsKey(key)) { - System.err.println("skipping contig " + cis.get(cii - 1)); - if (ci.getOrientation() >= 0) { - starts[cii] = chainLinkHashCut2.get(key); - starts2[cii] = chainLinkHashCut2a.get(key); - } else { - ends[cii] = chainLinkHashCut2.get(key); - ends2[cii] = chainLinkHashCut2a.get(key); - } - if (ci2.getOrientation() >= 0) { - ends[prevNN] = chainLinkHashCut1.get(key); - ends2[prevNN] = chainLinkHashCut1a.get(key); - } else { - starts[prevNN] = chainLinkHashCut1.get(key); - starts2[prevNN] = chainLinkHashCut1a.get(key); - } - } - } - } - } - - boolean toggleScaffolding = true; - for (int cii = 0; cii < cis.size(); ++cii) { - ContigInterval ci = cis.get(cii); - - boolean linked1 = false; // from chain file... - boolean linked2 = false; // from chain file... - if (cii > 0 && linkedIntervals(cis, cii - 1, cii)) - linked1 = true; - if (cii < cis.size() - 1 && linkedIntervals(cis, cii, cii + 1)) - linked2 = true; - - ContigInterval prev = ((linked1) ? cis.get(cii - 1): null); - ContigInterval next = ((linked2) ? cis.get(cii + 1): null); - - String map_info = ""; - String orientation = "?"; - - int posSupport = 0; - - for (int map = 0; map < numMaps; ++map) { - int max = Integer.MIN_VALUE; - int min = Integer.MAX_VALUE; - for (Marker m : ci.getMarkers(map)) { - max = Math.max(max, m.pPlus); - min = Math.min(min, m.pPlus); - posSupport += m.inside(m.pPlus); - } - if (max >= min) - map_info = map_info + "\t" + min + "-" + max; - else - map_info = map_info + "\t-"; - } - if (knownOrientation[cii]) - orientation = ((ci.getOrientation() >= 0) ? "+" : "-"); - else if (linked1 || linked2) { - if (!linked1) - toggleScaffolding = !toggleScaffolding; - if (!toggleScaffolding) - orientation = ((ci.getOrientation() >= 0) ? "++" : "--"); - else - orientation = ((ci.getOrientation() >= 0) ? "+++" : "---"); - } - - int scorePrev = 0; - int scoreNext = 0; - - String prevScaffoldInfo = "null"; - String nextScaffoldInfo = "null"; - - if (prev != null) { - String key = this.calculateKey(prev, ci); - if (chainLinkHash.containsKey(key)) { - scorePrev = chainLinkHash.get(key); - if (useChainAndPaf && scaffoldingLink.containsKey(key)) { - scorePrev += scaffoldingLink.get(key); - prevScaffoldInfo = "chain+" + scaffoldingLinkInfo.get(key); - } else - prevScaffoldInfo = "chain"; - } else if (scaffoldingLink.containsKey(key)) { - scorePrev = scaffoldingLink.get(key); - prevScaffoldInfo = scaffoldingLinkInfo.get(key); - } - int sp = ((prox == null) ? 0 : prox.linkScore(cis, cii - 1, cii)); - if (sp > 0) { - scorePrev +=sp ; - prevScaffoldInfo = prevScaffoldInfo + "+proximity"; - } - } - if (next != null) { - String key = this.calculateKey(ci, next); - if (chainLinkHash.containsKey(key)) { - scoreNext = chainLinkHash.get(key); - if (useChainAndPaf && scaffoldingLink.containsKey(key)) { - scoreNext += scaffoldingLink.get(key); - nextScaffoldInfo = "chain+" + scaffoldingLinkInfo.get(key); - } - else - nextScaffoldInfo = "chain"; - } else if (scaffoldingLink.containsKey(key)) { - scoreNext = scaffoldingLink.get(key); - nextScaffoldInfo = scaffoldingLinkInfo.get(key); - } - int sp = ((prox == null) ? 0 : prox.linkScore(cis, cii, cii + 1)); - if (sp > 0) { - scoreNext += prox.linkScore(cis, cii, cii + 1); - nextScaffoldInfo = nextScaffoldInfo + "+proximity"; - } - } // next != null - - //just get the start and end from the previously computed arrays... - long start = starts[cii]; - long end = ends[cii]; - long start2 = starts2[cii]; - long end2 = ends2[cii]; - - if (commentOutput) - stream.print("#"); - stream.println(ci.getContig() + "\t" + start + "\t" + end + "\t" + orientation + "\t" - + ci.getChromosome() + "\t" + orderSupport.get(cii) + "\t" + ci.getStart() + (start2==ci.getStart() ? "":"/" + start2) + "\t" + ci.getEnd() + (end2==ci.getEnd() ? "":"/" + end2) - + "\t" + (prev == null ? "null": prev.getContig()) + "\t" + prevScaffoldInfo + "\t" + scorePrev - + "\t" + (next == null ? "null": next.getContig()) + "\t" + nextScaffoldInfo + "\t" + scoreNext + "\t" + posSupport + map_info); - } - } - - - ArrayList logTable = new ArrayList(); - - private double myLog(int x) { - if (logTable.size() <= x) { - for (int i = logTable.size(); i <= x; ++i) - logTable.add(Math.log(i)); - } - return logTable.get(x); - } - - - - //markers is sorted within contig so no need to check orientation - private String getOneInterval(ArrayList markers, int m1, int m2) - { - Marker mm1 = markers.get(m1); - Marker mm2 = markers.get(m2); - ContigInterval ci = mm1.getContigInterval(); - long start = 0; - long end = 0; - - if (m1 == 0 || !ci.equals(markers.get(m1 - 1).getContigInterval())) - start = ci.getStart(); - else - start = (mm1.getPosition() + markers.get(m1 - 1).getPosition()) / 2; - - if (m2 + 1 == markers.size() || !ci.equals(markers.get(m2 + 1).getContigInterval())) - end = ci.getEnd(); - else - end = (mm2.getPosition() + markers.get(m1 + 1).getPosition()) / 2; - - return ci.getContig() + "\t" + start + "\t" + end; - } - - //markers is sorted - private String getIntervals(ArrayList markers, int m1, int m2) - { - String ret = ""; - ContigInterval prev = null; - int start = -1; - int end = -1; - for (int i = m1; i <= m2; ++i) { - Marker m = markers.get(i); - ContigInterval c = m.getContigInterval(); - if (c.equals(prev)) - end = i; - else { - if (start >= 0) - ret += '\t' + getOneInterval(markers, start, end); - start = i; - end = i; - } - prev = c; - } - return ret + '\t' + getOneInterval(markers, start, end); - } - - private class PossibleError implements Comparable{ - private double ll; - private String info; - private int ci; - private long pos1[], pos2[]; - public PossibleError(double ll, int ci, String info) { - this.ll = ll; - this.info = info; - this.ci = ci; - } - public PossibleError(double ll, int ci, String info, long pos1[], long pos2[]) { - this.ll = ll; - this.info = info; - this.ci = ci; - this.pos1 = pos1; - this.pos2 = pos2; - } - public String getInfo(){return info;} - @Override - public int compareTo(PossibleError other) { - if (ll < other.ll) - return 1; - if (ll > other.ll) - return -1; - return 0; - } - public int getCi() { - return ci; - } - public long[] getPos1() { - return pos1; - } - public long[] getPos2() { - return pos2; - } - } - - // log( (a/(a+b))^a * (b/(a+b))^b) - private double logLike(int a, int b) - { - double ret = 0; - if (a > 0) - ret += a * (myLog(a) - myLog(a + b)); - if (b > 0) - ret += b * (myLog(b) - myLog(a + b)); - return ret; - } - - // log( (a/(a+b))^c * (b/(a+b))^d) - private double logLike(int a, int b, int c, int d) - { - double ret = 0; - if (c > 0) - ret += c * (myLog(a) - myLog(a + b)); - if (d > 0) - ret += d * (myLog(b) - myLog(a + b)); - return ret; - } - - // find max maxNumErrors regions with more errors than expected, error rate is e0/(e0 + e1) - private void calculateErrors2(ArrayList markers_, ArrayList errors, int e0, int e1, int maxNumErrors) - { - boolean inside[] = new boolean[markers_.size()]; - int cumulative[] = new int[markers_.size() + 1]; - - int sum = 0; - for (int mi = 0; mi < markers_.size(); ++mi) { - Marker m = markers_.get(mi); - if (m.inside(m.pPlus) > 0) - inside[mi] = true; - else - ++sum; - cumulative[mi + 1] = sum; - } - if (sum == 0) // all inside or no markers - return; - - ArrayList markers = new ArrayList(); - - for (int mi = 0; mi < markers_.size(); ++mi) { // take only first and last marker of multiple adjacent markers !inside - if ((mi == 0 || inside[mi - 1]) && !inside[mi]) - markers.add(mi); - else - if ((mi + 1 == markers_.size() || inside[mi + 1]) && !inside[mi]) - markers.add(mi); - } - //System.err.println(markers.size() + "\t" + markers_.size()); - - int numMarkers = markers.size(); - - ArrayList found = new ArrayList(); - for (int iteration = 0; iteration < maxNumErrors; ++iteration) { - double max = 0; - int max1 = -1; - int max2 = -1; - int maxo0 = 0; - int maxo1 = 0; -out2: for (int m1i = 0; m1i < numMarkers; ++m1i) { - int m1 = markers.get(m1i); - for (int[] f : found) - if (f[1] >= m1 && f[0] <= m1) // found intervals contain m1 - continue out2; - for (int m2i = m1i; m2i < numMarkers; ++m2i) { - int m2 = markers.get(m2i); - for (int[] f : found) - if (f[1] >= m2 && f[0] <= m2) // found intervals intersect m2 ([m1...m2]) - continue out2; - int o0 = cumulative[m2 + 1] - cumulative[m1]; // number of markers outside interval - int o1 = (m2 - m1 + 1) - o0; // number of markers within interval - //o0 * (e0 + e1) > e0 * (o0 + o1) (positive e and o) <=> o0 / (o0 + o1) > e0 / (e0 + e1) - if ( o0 * (long)(e0 + e1) > e0 * (long)(o0 + o1)) { //more errors than expected - double l = logLike(o0, o1) - logLike(e0, e1, o0, o1); //likelihood ratio - if (l > max) { - max = l; - max1 = m1; - max2 = m2; - maxo0 = o0; - maxo1 = o1; - } - } else - continue out2; // no need to continue if we fall short - } - } - if (max > 0) { - found.add(new int[]{max1, max2}); - ContigInterval ci = markers_.get(max1).getContigInterval(); - long pos1 = markers_.get(max1).getPosition(); - long pos1n = ((max1 > 0) ? markers_.get(max1 - 1).getPosition() + 1 : ci.getStart()); - - long pos2 = markers_.get(max2).getPosition(); - long pos2n = ((max2 + 1 < markers_.size()) ? markers_.get(max2 + 1).getPosition() - 1 : ci.getEnd()); - - //if (pos1 < pos1n || pos2 > pos2n) { - // System.exit(-1); - //} - - String info = max + "\t" + maxo0 + "\t" + maxo1 + "\t" + ci.getContig() + "\t" + pos1n + "-" + pos1 + "\t" + pos2 + "-" + pos2n; - errors.add(new PossibleError(max, ci.getRank(), info , new long[]{pos1n, pos1}, new long[]{pos2, pos2n})); - } - } - } - - - - private ArrayList calculateBinSum(ArrayList markers, int endBin, boolean plusOrientation){ - //calculate map position distribution - ArrayList binSum = new ArrayList(); - for (int i = 0; i <= endBin; ++i) - binSum.add(0); - - //int plus = 0; - //int minus = 0; - for (Marker m : markers) { - if (plusOrientation) - binSum.set(m.pPlus, binSum.get(m.pPlus) + 1); - else - binSum.set(m.pMinus, binSum.get(m.pMinus) + 1); - } - return binSum; - } - - private class LongComparator0 implements Comparator - { - @Override - public int compare(long[] l1, long[] l2){ - return Long.compare(l1[0], l2[0]); - } - } - - private class LongComparator1 implements Comparator - { - @Override - public int compare(long[] l1, long[] l2){ - return Long.compare(l1[1], l2[1]); - } - } - - //calculate how much score could be increasing if we split a contig... - //make a gap in map positions L..R, and see how many points we could add there - private ArrayList increaseScore(ArrayList markers, ArrayList binSum){ - - ArrayList ret = new ArrayList(); - - int endBin = getMaxBin(markers); - if (endBin < binSum.size() - 1) - endBin = binSum.size() - 1; - int startBin = getMinBin(markers); - if (startBin > 0) - startBin = 0; - - int numMarkers = markers.size(); - - int S[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - int Sp[] = new int[endBin - startBin + 1]; - - int Sb[][] = new int[numMarkers + 1][endBin - startBin + 1]; - - //simple way to calculate (about correct) backward table... - Collections.reverse(markers); - forwardFast2(markers, Sb, startBin, endBin); - Collections.reverse(markers); - forwardFast1(markers, S, startBin, endBin); - //Should we make S and Sb monotonic (+/- 1) ? - - for (int i = 0; i <= numMarkers; ++i) { - int max = 0; - //int maxL = 0; - //int maxR = 0; - - //reuse P as PSp - for (int b = 0; b < endBin - startBin; ++b) { // add binSum to S - int bs = 0; - if (b + startBin < binSum.size()) - bs = binSum.get(b + startBin); - - //either we take score S[i][b]+bs or skip b and get bs + score Sp[b-1] - if (b == 0) { - Sp[b] = S[i][b] + bs; - //P[i][b] = b; - } else { - int s1 = S[i][b] + bs; - int s2 = Sp[b - 1] + bs; - if (s1 >= s2) { - Sp[b] = s1; - //P[i][b] = b; - } else { - Sp[b] = s2; - //P[i][b] = P[i][b - 1]; - } - } - } - - - for (int b = 0; b <= endBin - startBin; ++b) { - int s = Sp[b] + Sb[numMarkers - i][b]; - if (s > max) { - max = s; - //maxR = P[i][b] + startBin; - //maxL = b + startBin; - } - } - ret.add(max); - } - return ret; - } - - - private ArrayList findNonHaplotypeAssemblyErrors(ArrayList cis, boolean verbose) - { - ArrayList ret = new ArrayList(); - - ArrayList errors = new ArrayList(); - int e0 = 0; - int e1 = 0; - for (int iteration = 0; iteration < 2; ++iteration) { //first iteration: calculate e0 and e1, second iteration find errors... - for (int cii = 0; cii < cis.size(); ++cii) { - ContigInterval ci = cis.get(cii); - - //Haplotypes are typical sources of errors, - //by adjusting start and end, one could reduce errors caused by them... - //best way would be to remove markers from one pair (with less markers) of haplotype and re-evaluate the maps... -/* - long start = ci.getStart(); - long end = ci.getEnd(); - int orientation = ci.getOrientation(); - if (cii > 0) { - ContigInterval prev = cis.get(cii - 1); - String key = calculateKey(prev, ci); - Long cut = chainLinkHashCut2.get(key); //Cut2a - if (cut != null) - if (orientation >= 0) - start = cut; - else - end = cut; - } - if (cii + 1 < cis.size()) { - ContigInterval next = cis.get(cii + 1); - String key = calculateKeyInverse(ci, next); - Long cut = chainLinkHashCut2.get(key); //Cut2a - if (cut != null) - if (orientation >= 0) - end = cut; - else - start = cut; - }*/ - - if (iteration == 0) { - for (int map = 0; map < numMaps; ++map) - for (Marker m : ci.getMarkers(map)) - //if (m.getPosition() >= start && m.getPosition() <= end) - if (m.inside(m.pPlus) > 0) // TODO: allow any scores, now 0 vs >0 - ++e1; - else - ++e0; - - } else { - //System.err.println(e1 + "\t" + e0); - ArrayList markersCi = new ArrayList(); - for (int map = 0; map < numMaps; ++map) - for (Marker m : ci.getMarkers(map)) - markersCi.add(m); - Collections.sort(markersCi); - //calculateErrors(markersCi, errors, e0, e1); - calculateErrors2(markersCi, errors, e0, e1, numErrorsPerContig); - } - } - } - - if (verbose) { - System.err.println("#*** possible errors ***"); - System.err.println("#total outside/inside:\t" + e0 + "\t" + e1); - System.err.println("#X2\t0\t>0\tcontig\tstart\tend\tcontig2\tpos"); - } - - Collections.sort(errors); - if (errors != null) - for (int i = 0; i < numErrors && i < errors.size(); ++i) { - ret.add(errors.get(i)); - PossibleError ff = findFix(cis, errors.get(i), false); - ret.add(ff); - if (verbose) { - System.err.println(errors.get(i).getInfo() + ((ff == null) ? "" : "\t" + ff.getInfo()) + "\terror"); - } - } - return ret; - } - - //TODO: Check that calculateChainScore is ok - //note: assumes that score has been evaluated for the markers in cis and in the corresponding order and orientation - private void findLikelyAssemblyErrors(ArrayList cis) - { - System.err.println("*** possible haplotypes ***"); - System.err.println("score\tcontig\tstart\tend\tof_contig\tstart\tend\tcontig_aligment_start\tcontig_aligment_end"); - - ArrayList haplotypes = new ArrayList(); - - for (int cii = 0; cii < cis.size(); ++cii) { - ContigInterval ci = cis.get(cii); - int score = 0; - for (int map = 0; map < numMaps; ++map) - for (Marker m : ci.getMarkers(map)) - score += m.inside(m.pPlus); - if (cii > 0) - score += calculateChainScore(cis.get(cii - 1), ci); - if (cii + 1 < cis.size()) - score += calculateChainScore(ci, cis.get(cii + 1)); - - //int scoreH = 0; - //ContigInterval haplotypeOf = null; - for (ContigInterval ci2 : cis) { - Integer s = calculateChainScoreHaplotype(ci2, ci); - if (s != null && s > score) { - long ha[] = calculateChainScoreHaplotypeCut(ci2, ci); - haplotypes.add(new PossibleError((s - score), 0, (s - score) + "\t" + ci + "\t" + ci2 + "\t" + ha[0] + "\t" + ha[1])); -// scoreH = s; -// haplotypeOf = ci2; - } - } -// if (scoreH > score) { -// haplotypes.add(new PossibleError((scoreH - score), (scoreH - score) + "\t" + ci + "\t" + haplotypeOf)); -// //System.err.println((scoreH - score) + "\t" + ci + "\t" + haplotypeOf); -// } - - } - Collections.sort(haplotypes); - for (PossibleError pe: haplotypes) - System.err.println(pe.getInfo() + "\thaplotype"); - - findNonHaplotypeAssemblyErrors(cis, true); - - } - void setMaxIntersect(int parseInt) { - maxIntersect = parseInt; - } - - void setMinLinkAlignmentScore(int parseInt) { - minLinkAlignmentScore = parseInt; - - } - - private void setMinHaplotypeAlignmentScore(int parseInt) { - minHaplotypeAlignmentScore = parseInt; - } - - //autogenerated stubs... - void setMaxBridge(int parseInt) { - maxBridge = parseInt; - } - - void setCutPenalty(double parseDouble) { - cutPenalty = parseDouble; - } - - void setOrientationPenalty(double parseDouble) { - orientationPenalty = parseDouble; - } - - void setScaleScore(double parseDouble) { - scaleScore = parseDouble; - } - void setNumThreads(int nt) { - numThreads = nt; - } - - private ArrayList> myLiftover(ArrayList> markers, ArrayList alignment, boolean sameOrientation, String newContig) - { - ArrayList> ret = new ArrayList>(); - for (ArrayList row: markers) { - //System.err.println(row); - long position = InputData.myParseLong(row.get(1)); - long position_new = mapPosition12(alignment, position, sameOrientation); - //System.err.println(position + "->" + position_new); - if (position_new > 0) { - //System.err.println("HIPHEI"); - ArrayList nrow = new ArrayList(); - for (int i = 0; i < row.size(); ++i) - if (i == 0) - nrow.add(newContig); - else if (i == 1) - nrow.add("" + position_new); - else - nrow.add(row.get(i)); - ret.add(nrow); - } - } - return ret; - } - - private void liftover__(ContigInterval hap, ContigInterval c2, ArrayList alignment, boolean sameOrientation, long ascore, HashMap bestScore, HashMap bestScoreContig, HashMap>> map, HashMap>> liftoverMap) { - Long prevScore = bestScore.get(hap); - if (prevScore != null) { - ContigInterval c3 = bestScoreContig.get(hap); - if (c3.getContig().equals(c2.getContig()) && Misc.intersectIntervals(c2.getStart(), c2.getEnd(), c3.getStart(), c3.getEnd())) { // same alignment... - if (ascore > prevScore) { - liftoverMap.put(hap, myLiftover(map.get(hap), alignment, sameOrientation, c2.getContig())); // do liftover - bestScoreContig.put(hap, c2); - bestScore.put(hap, ascore); - } - } else { - if (ascore > prevScore) { - bestScoreContig.put(hap, c2); - bestScore.put(hap, ascore); - } - if (ascore > 2 * prevScore) { - liftoverMap.put(hap, myLiftover(map.get(hap), alignment, sameOrientation, c2.getContig())); // do liftover - } - else if (ascore > liftoverScoreDiff * prevScore) {// too little score difference... - liftoverMap.get(hap).clear(); // remove markers... - //System.err.println("remove"); - } - } - - } else { - //System.err.println("liftover"); - liftoverMap.put(hap, myLiftover(map.get(hap), alignment, sameOrientation, c2.getContig())); // do liftover - bestScore.put(hap, ascore); - bestScoreContig.put(hap, c2); - } - } - - private void liftover_(String fn, HashMap>> map) { - System.err.println("loading alignment chain..."); - - HashMap bestScore = new HashMap(); // score of used chain - HashMap bestScoreContig = new HashMap(); // contigInterval of used chain - - HashMap>> liftoverMap = new HashMap>>(); // store liftover markers... - - for (ContigInterval ci : map.keySet()) { - liftoverMap.put(ci, new ArrayList>()); - //System.err.println(ci); - } - - try { - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - do { - ArrayList row = Input.loadTableRow(br, "\t "); - if (row == null) - break; - boolean skip = true; - if (row.size() >= 12 && "chain".equals(row.get(0))) { - String contig1 = row.get(2); - String contig2 = row.get(7); - - boolean someMarkers1 = false; - if (haplotypeHash.containsKey(contig1)) - for (ContigInterval hap : haplotypeHash.get(contig1)) - if (map.containsKey(hap)) { - someMarkers1 = true; - break; - } - boolean someMarkers2 = false; - if (haplotypeHash.containsKey(contig2)) - for (ContigInterval hap : haplotypeHash.get(contig2)) { - if (map.containsKey(hap)) { - someMarkers2 = true; - break; - } - } - if (!someMarkers1 && !someMarkers2) // no markers to do liftover... - continue; - - skip = false; - long p1_[] = chain2OneBase(row.get(3), row.get(4), row.get(5), row.get(6)); - long p2_[] = chain2OneBase(row.get(8), row.get(9), row.get(10), row.get(11)); - if ("-".equals(row.get(4))) { - System.err.println("Error: only ++, and +- orientation allowed in the chain file"); - continue; - } - - //System.err.println("running"); - - boolean sameOrientation = row.get(4).equals(row.get(9)); - - // load alignment for liftover... - ArrayList alignment_ = new ArrayList(); - ArrayList row2 = null; - long v1 = p1_[0]; - long v2 = ((sameOrientation) ? p2_[0] : p2_[1]); - int v3 = 0; - do { - row2 = Input.loadTableRow(br, "\t "); - - v3 = Integer.parseInt(row2.get(0)); - alignment_.add(new long[]{v1, v2, v3}); - v1 += v3; - v2 += ((sameOrientation) ? v3 : -v3); - if (row2.size() >= 3) { - v1 += Integer.parseInt(row2.get(1)); - v2 += ((sameOrientation) ? Integer.parseInt(row2.get(2)) : -Integer.parseInt(row2.get(2))); - } - } while (row2 != null && row2.size() >= 3); - - long ascore_ = Long.parseLong(row.get(1)); - - if (someMarkers1) // && haplotypeHash.containsKey(contig1) - for (ContigInterval hap : haplotypeHash.get(contig1)) - if (map.containsKey(hap)) { - ContigInterval c2 = new ContigInterval(contig2, p2_[0], p2_[1]); - - ArrayList alignment = new ArrayList(); - double t = trimChain(alignment_, sameOrientation, hap, c2, alignment); - if (t == 0.0) // all trimmed - continue; - - if (t < 1.0) { // update c2 start and end - long p[] = getStartEnd2(alignment, sameOrientation); - c2 = new ContigInterval(contig2, p[0], p[1]); - } else { - alignment = alignment_; - } - - long ascore = (long)(t * ascore_); - - boolean doLift = true; - if (haplotypeHash.containsKey(contig2)) - for (ContigInterval hap2 : haplotypeHash.get(contig2)) // no liftover between haplotypes... - if (Misc.intersectIntervals(hap2.getStart(), hap2.getEnd(), c2.getStart(), c2.getEnd())) { - doLift = false; - break; - } - if (doLift) - liftover__(hap, c2, alignment, sameOrientation, ascore, bestScore, bestScoreContig, map, liftoverMap); - } - //otherway round... - if (someMarkers2) { // && haplotypeHash.containsKey(contig2) - ArrayList revAlignment_ = reverseAlignment(alignment_, sameOrientation); - for (ContigInterval hap : haplotypeHash.get(contig2)) - if (map.containsKey(hap)) { - ContigInterval c2 = new ContigInterval(contig1, p1_[0], p1_[1]); - - ArrayList alignment = new ArrayList(); - double t = trimChain(revAlignment_, sameOrientation, hap, c2, alignment); - if (t == 0.0) // all trimmed - continue; - if (t < 1.0) { // update c2 start and end - long p[] = getStartEnd2(alignment, sameOrientation); - c2 = new ContigInterval(contig1, p[0], p[1]); - } else - alignment = revAlignment_; - long ascore = (long)(t * ascore_); - - boolean doLift = true; - if (haplotypeHash.containsKey(contig1)) - for (ContigInterval hap2 : haplotypeHash.get(contig1)) // no liftover between haplotypes... - if (Misc.intersectIntervals(hap2.getStart(), hap2.getEnd(), c2.getStart(), c2.getEnd())) { - doLift = false; - break; - } - if (doLift) - liftover__(hap, c2, alignment, sameOrientation, ascore, bestScore, bestScoreContig, map, liftoverMap); - } - } - } else { - if (skip && (row.size() == 3 || row.size() == 1)) - ; - else - System.err.println("Warning: skipping " + row); - } - } while (true); - br.close(); - int numLifoverMarkers = 0; - for (ContigInterval ci : liftoverMap.keySet()) { - ArrayList> lom = liftoverMap.get(ci); - numLifoverMarkers += lom.size(); - StringBuilder sb = new StringBuilder(); - for (ArrayList row : lom) { - sb.append(row.get(0)); - for (int i = 1; i < row.size(); ++i) { - sb.append('\t'); - sb.append(row.get(i)); - } - sb.append('\n'); - } - System.out.print(sb); - } - System.err.println("Lifting over " + numLifoverMarkers + " markers"); - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - } - } - - public void liftover(String haplotypeFile, String chainFile, String mapFile) { - ArrayList haplotypes = InputData.loadHaplotypes(haplotypeFile); - - for (ContigInterval hapci: haplotypes) { - String key = hapci.getContig(); - ArrayList list = haplotypeHash.get(key); - if (list == null) { - list = new ArrayList(); - haplotypeHash.put(key, list); - } - list.add(hapci); - } - - HashMap>> map = InputData.loadRaw(mapFile, haplotypeHash); - //System.out.println(map); - - liftover_(chainFile, map); - - } - - private void loadCutSites(String fn) { - try { - HashMap> bedHash2 = new HashMap>(); - for (ContigInterval ci : bed) { - if (ci.getStart() != ci.getMinStart() || ci.getEnd() != ci.getMaxEnd()) { // ContigInterval with uncertainty in its start or end - String contig = ci.getContig(); - if (!bedHash2.containsKey(contig)) - bedHash2.put(contig, new ArrayList()); - bedHash2.get(contig).add(ci); - } - } - int numCutSites = 0; - BufferedReader br = new BufferedReader(new FileReader(fn)); - do { - ArrayList row = Input.loadTableRow(br, "\t "); - if (row == null) - break; - if (row.size() >= 2) { - String contig = row.get(0); - long position = 0; - long position2 = 0; - if (bedHash2.containsKey(contig)) - for (ContigInterval ci : bedHash2.get(contig)) { - if (position == 0) { - position = Long.parseLong(row.get(1)); - if (row.size() >= 3) { - position2 = Long.parseLong(row.get(2)); - --position; //N gap - ++position2; - } else - position2 = position + 1; - } - - - if (position2 >= ci.getMinStart() && position2 <= ci.getMaxStart()) { - ++numCutSites; - ci.setStart(position2); - break; - } - if (position >= ci.getMinEnd() && position <= ci.getMaxEnd()) { - ++numCutSites; - ci.setEnd(position); - break; - } - } - } - } while (true); - br.close(); - System.err.println("Trimming " + numCutSites + " contig ends based on cutSites file"); - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - } - } - - //find how to fix likely erroneous genomic interval (e) - //assumes solve has been called on the cis - private PossibleError findFix(ArrayList cis, PossibleError e, boolean verbose){ - //update rank and numContigIntervals - int numContigIntervals = 0; // update rank (might be corrupted) - for (ContigInterval c : cis) - c.setRank(numContigIntervals++); - - //calculate full anchoring score - ArrayList oldPos = new ArrayList(); - int oldFullScore = 0; - for (int map = 0; map < numMaps; ++map) - for (Marker m : getMarkers(cis, null, map)) { - oldFullScore += m.inside(m.pPlus); - oldPos.add(m.pPlus); // store mPlus, as this is changed... - } - - //get error region (region inside one contigInterval) - ContigInterval errorContig = cis.get(e.getCi()); - long pos1[] = e.getPos1(); - long pos2[] = e.getPos2(); - - int s0 = 0; // old score for region e - int s1 = 0; // score for e in + orient - int s2 = 0; // score for e in - orient - - ArrayList> iss = new ArrayList>(); //store increaseScores... - - for (int map = 0; map < numMaps; ++map) { - //get markers spanning error region for each map separately - - ArrayList errorMarkers = new ArrayList(); - ArrayList nonErrorMarkers = new ArrayList(); - - for (ContigInterval c : cis) - for (Marker m : c.getMarkers(map)) { - if ((c == errorContig) && m.getPosition() >= pos1[0] && m.getPosition() <= pos2[1]) - errorMarkers.add(m); - else - nonErrorMarkers.add(m); - } - - int numMarkers = errorMarkers.size(); - //calculate old score - //int oldScore[] = new int[numMarkers]; - for (int i = 0; i < numMarkers; ++i) { - Marker m = errorMarkers.get(i); - int s = m.inside(m.pPlus); - s0 += s; - } - s1 += solveForward(errorMarkers); - s2 += solveBackward(errorMarkers); - - ArrayList mm2 = new ArrayList(); - for (int i = 0; i < numMarkers; ++i) { - Marker m = errorMarkers.get(i); - if (m.inside(m.pPlus) > 0) // marker is now contributing to the score - mm2.add(m); - } - int maxBin = getMaxBin(errorContig.getMarkers(map)); - - iss.add(increaseScore(nonErrorMarkers, calculateBinSum(mm2, maxBin, true))); // mm2 in + orientation - - mm2.clear(); - for (int i = 0; i < numMarkers; ++i) { - Marker m = errorMarkers.get(i); - if (m.inside(m.pMinus) > 0) // marker is now contributing to the score - mm2.add(m); - } - - iss.add(increaseScore(nonErrorMarkers, calculateBinSum(mm2, maxBin, false))); // mm2 in - orientation - - //System.err.println("size=" + nonErrorMarkers.size()); - - } - - int pi = 0; - for (int map = 0; map < numMaps; ++map) - for (Marker m : getMarkers(cis, null, map)) - m.pPlus = oldPos.get(pi++); // restore mPlus, this is changed by increaseScore - - if ((s0 >= s1 && s0 >= s2)) - return null; // no fix found... - - //System.err.println("max score delta = " + (s1 - s0) + " or " + (s2 - s0) + "\t" + s0 + "\t" + s1 + "\t" + s2 + "\t" + e.getInfo()); - //merge results from each increaseScore - - long maxScore = oldFullScore; - int maxC = 0; - long maxP1 = 0; - long maxP2 = 0; - - for (int orient = 0; orient < 2; ++orient) { - int startMarker[] = new int[numMaps]; - for (ContigInterval c : cis) { - //store cut intervals - //if interval [a,b] is in cov, then prefix can be [1..a], [1..a+1],...,[1..b] - ArrayList cov = new ArrayList(); - - for (int map = 0; map < numMaps; ++map) { - ArrayList is = iss.get(map + map + orient); - - //System.err.println("size=" + is.size()); - - ArrayList markers = new ArrayList(); - for (Marker m : c.getMarkers(map)) - if ((errorContig == c) && m.getPosition() >= pos1[0] && m.getPosition() <= pos2[1]) // errorMarker - ; - else - markers.add(m); - - //System.err.println("size=" + markers.size()); - - int numMarkers = markers.size(); - if (numMarkers > 0) { - long isi = is.get(startMarker[map]); - if (c.getOrientation() >= 0) { // + orientation - cov.add(new long[]{c.getStart() - 1, markers.get(0).getPosition() - 1, isi}); - for (int mi = 1; mi < numMarkers; ++mi) { - //Marker m = markers.get(mi); - isi = is.get(startMarker[map] + mi); - cov.add(new long[]{markers.get(mi - 1).getPosition(), markers.get(mi).getPosition() - 1, isi}); - } - isi = is.get(startMarker[map] + numMarkers); - cov.add(new long[]{markers.get(numMarkers - 1).getPosition(), c.getEnd(), isi}); - } else { - cov.add(new long[]{markers.get(0).getPosition(), c.getEnd(), isi}); - for (int mi = 1; mi < numMarkers; ++mi) { - //Marker m = markers.get(mi); - isi = is.get(startMarker[map] + mi); - cov.add(new long[]{markers.get(mi).getPosition(), markers.get(mi - 1).getPosition() - 1, isi}); - } - isi = is.get(startMarker[map] + numMarkers); - cov.add(new long[]{c.getStart() - 1, markers.get(numMarkers - 1).getPosition() - 1, isi}); - } - } else { // 0 markers - int isi = is.get(startMarker[map]); - cov.add(new long[]{c.getStart() - 1, c.getEnd(), isi}); - } - startMarker[map] += numMarkers; - } - ArrayList cov_ret = Misc.cov(cov); - //System.err.println(cov_ret); - - for (int i = 0; i < cov_ret.size(); i+=2) { - long pos = cov_ret.get(i); - long cr = cov_ret.get(i + 1); - //System.err.println(pos + "\t" + cr); - if (cr > maxScore) { - maxScore = cr; - maxC = c.getRank(); - maxP1 = pos; - maxP2 = ((i + 2 < cov_ret.size()) ? cov_ret.get(i + 2) - 1 : maxP1); - } - } - } - } - - if (verbose) - System.err.println(cis.get(maxC).getContig() + "\t" + maxP1 + "-" + maxP2 + "\t" + errorContig.getContig() + "\t" + pos1[0] + "-" + pos1[1] + "\t" + pos2[0] + "-" + pos2[1] + "\t" + (maxScore - oldFullScore) + "\tfix"); - - String info = cis.get(maxC).getContig() + "\t" + maxP1 + "-" + maxP2; - - if (maxScore <= oldFullScore) { - return null; - } - return new PossibleError((maxScore - oldFullScore), maxC, info , new long[]{maxP1, maxP2}, null); - } - - // find a region inside an error with even higher error rate - private PossibleError splitError(ContigInterval c, PossibleError e) - { - //PossibleError ret = new PossibleError(max, ci.getRank(), info , new long[]{pos1n, pos1}, new long[]{pos2, pos2n} - long pos1[] = e.getPos1(); - long pos2[] = e.getPos2(); - ArrayList markersC = new ArrayList(); - int e0 = 0; - int e1 = 0; - for (int map = 0; map < numMaps; ++map) - for (Marker m : c.getMarkers(map)) - if (m.getPosition() >= pos1[0] && m.getPosition() <= pos2[1]) { - if (m.inside(m.pPlus) > 0) - ++e1; - else - ++e0; - markersC.add(m); - } - Collections.sort(markersC); - //System.err.println("e0=" + e0 + "\te1=" + e1); - - //calculateErrors(markersCi, errors, e0, e1); - ArrayList pes = new ArrayList(); - calculateErrors2(markersC, pes, e0, e1, 1); - if (pes.size() > 0) { - PossibleError ret = pes.get(0); - long p1[] = ret.getPos1(); // adjust region so that it is within the original error... - if (p1[0] < pos1[0]) - p1[0] = pos1[0]; - long p2[] = ret.getPos2(); - if (p2[1] > pos2[1]) - p2[1] = pos2[1]; - ret.ci = e.ci; // rank might be corrupted so calculateErrors2 could fail to get this right - return ret; - } - - return null; - } - //iterative version of findLikelyAssemblyErrors - private void findContigErrors(int minImprovement) { - System.err.println("Finding contig errors..."); - ArrayList newcis = new ArrayList(); - - int animation = 0; - boolean foundFix = true; - - HashMap missedErrors = new HashMap(); - - while (foundFix) { - foundFix = false; - int scoreOld = calculateScore(intervalsInChr, false) + calculateNonMarkerScore(intervalsInChr); - - int orientation[] = new int[intervalsInChr.size()]; // store orientaions - for (int ci = 0; ci < orientation.length; ++ci) - orientation[ci] = intervalsInChr.get(ci).getOrientation(); - - //ArrayList pe = findNonHaplotypeAssemblyErrors(intervalsInChr, false); - ArrayList pe = findNonHaplotypeAssemblyErrors(intervalsInChr, false); - - System.err.println("score = " + scoreOld); - - out:for (int e = 0; e + e < pe.size(); ++e) { - PossibleError pe1 = pe.get(e + e); - ContigInterval c1 = intervalsInChr.get(pe1.getCi()); - - PossibleError pe2 = pe.get(e + e + 1); - String errorId = pe1.getInfo() + ((pe2 == null) ? "" : "\t" + pe2.getInfo()); - - // see if we have already tried to fix this error, skip with decreasing prob - String errorId2 = errorId.substring(errorId.indexOf('\t')); - if (missedErrors.containsKey(errorId2)) { - double newValue = missedErrors.get(errorId2) * 0.5; - if (Math.random() >= newValue) - continue; - missedErrors.put(errorId2, newValue); - } else - missedErrors.put(errorId2, 2.0); // try twice, then with pr 1/2, 1/4, 1/8, ... - - System.err.println(errorId); - for (int fixIteration = 0; fixIteration < 2; ++fixIteration) { // try to split each region in fixIteration==1 - if (fixIteration == 1) { - calculateScore(intervalsInChr, false); // update pPlus... - pe1 = splitError(c1, pe1); - if (pe1 == null) { - //System.err.println("no split"); - break; - } - pe2 = findFix(intervalsInChr, pe1, false); - errorId = pe1.getInfo() + ((pe2 == null) ? "" : "\t" + pe2.getInfo()); - System.err.println(errorId); - } - ContigInterval sp1[] = null; - ContigInterval sp2[] = null; - ContigInterval c2 = null; - - long p11[] = pe1.getPos1(); - p11[0] -= 1; //-1 - p11[1] -= 1; //-1 - if (pe2 != null) { - c2 = intervalsInChr.get(pe2.getCi()); - if (c1 == c2) { - sp1 = c1.splitContigInterval(p11, pe1.getPos2(), pe2.getPos1()); - } else { - sp1 = c1.splitContigInterval(p11, pe1.getPos2()); - sp2 = c2.splitContigInterval(pe2.getPos1()); - } - } else { - sp1 = c1.splitContigInterval(p11, pe1.getPos2()); - } - - newcis.clear(); - for (ContigInterval ci : intervalsInChr) { - if (ci != c1 && ci != c2) - newcis.add(ci); - else if (ci == c1) - for (ContigInterval c : sp1) - newcis.add(c); - else - for (ContigInterval c : sp2) - newcis.add(c); - } - //System.err.println(newcis); - int splitSize1 = sp1.length; - int splitSize2 = ((sp2 == null) ? 1 : sp2.length); - if (splitSize1 == 1 && splitSize2 == 1) // no split... - continue; - - - //add scaffolding links - for (int ci = 1; ci < sp1.length; ++ci) { - String key1 = calculateKey(sp1[ci - 1], true, sp1[ci], true); - String key2 = calculateKey(sp1[ci], false, sp1[ci - 1], false); - scaffoldingLink.put(key1, 1); - scaffoldingLink.put(key2, 1); - } - if (sp2 != null) - for (int ci = 1; ci < sp2.length; ++ci) { - String key1 = calculateKey(sp2[ci - 1], true, sp2[ci], true); - String key2 = calculateKey(sp2[ci], false, sp2[ci - 1], false); - scaffoldingLink.put(key1, 1); - scaffoldingLink.put(key2, 1); - } - //keep old scaffoldingLinks - for (ContigInterval c : intervalsInChr) { - String k1 = calculateKey(c1, true, c, true); - if (scaffoldingLink.containsKey(k1)) { - scaffoldingLink.put(calculateKey(sp1[sp1.length - 1], true, c,true), 1); - scaffoldingLink.put(calculateKey(c, false, sp1[sp1.length - 1], false), 1); - } - String k2 = calculateKey(c, true, c1, true); - if (scaffoldingLink.containsKey(k2)) { - scaffoldingLink.put(calculateKey(c, true, sp1[0], true), 1); - scaffoldingLink.put(calculateKey(sp1[0], false, c, false), 1); - } - if (sp2 != null) { - k1 = calculateKey(c2, true, c, true); - if (scaffoldingLink.containsKey(k1)) { - scaffoldingLink.put(calculateKey(sp2[sp2.length - 1], true, c,true), 1); - scaffoldingLink.put(calculateKey(c, false, sp2[sp2.length - 1], false), 1); - } - k2 = calculateKey(c, true, c2, true); - if (scaffoldingLink.containsKey(k2)) { - scaffoldingLink.put(calculateKey(c, true, sp2[0], true), 1); - scaffoldingLink.put(calculateKey(sp2[0], false, c, false), 1); - } - } - } - //keep old chainLinks - for (ContigInterval c : intervalsInChr) { - for (int o = 0; o < 2; ++o) - for (int o1 = 0; o1 < 2; ++o1) { - String k1 = calculateKey(c, o == 0, c1, o1 == 0); - if (chainLinkHash.containsKey(k1)) { - String knew = calculateKey(c, o == 0, (o1==0) ? sp1[0] : sp1[sp1.length - 1], o1 == 0); - chainLinkHash.put(knew, chainLinkHash.get(k1)); - } - String k2 = calculateKey(c1, o1 == 0, c, o == 0); - if (chainLinkHash.containsKey(k2)) { - String knew = calculateKey((o1==0) ? sp1[sp1.length - 1]: sp1[0], o1 == 0, c, o == 0); - chainLinkHash.put(knew, chainLinkHash.get(k2)); - } - if (sp2 != null) { - String k3 = calculateKey(c, o == 0, c2, o1 == 0); - if (chainLinkHash.containsKey(k3)) { - String knew = calculateKey(c, o == 0, (o1==0) ? sp2[0] : sp2[sp2.length - 1], o1 == 0); - chainLinkHash.put(knew, chainLinkHash.get(k3)); - } - String k4 = calculateKey(c2, o1 == 0, c, o == 0); - if (chainLinkHash.containsKey(k4)) { - String knew = calculateKey((o1==0) ? sp2[sp2.length - 1]: sp2[0], o1 == 0, c, o == 0); - chainLinkHash.put(knew, chainLinkHash.get(k4)); - } - - } - - } - } - if (sp2 != null) // keep chain links between c1 to c2 - for (int o1 = 0; o1 < 2; ++o1) - for (int o2 = 0; o2 < 2; ++o2) { - String k1 = calculateKey(c1, o1 == 0, c2, o2 == 0); - if (chainLinkHash.containsKey(k1)) { - String knew = calculateKey((o1==0) ? sp1[sp1.length - 1]: sp1[0], o1 == 0, (o2==0) ? sp2[0]: sp2[sp2.length - 1], o2 == 0); - chainLinkHash.put(knew, chainLinkHash.get(k1)); - } - String k2 = calculateKey(c2, o2 == 0, c1, o1 == 0); - if (chainLinkHash.containsKey(k2)) { - String knew = calculateKey((o2==0) ? sp2[sp2.length - 1]: sp2[0], o2 == 0, (o1==0) ? sp1[0]: sp1[sp1.length - 1], o1 == 0); - chainLinkHash.put(knew, chainLinkHash.get(k2)); - } - } - - - - //improveAnchoring(newcis, false, false); - - //faster version of improveAnchoring - { - int bestScore = -1; - ArrayList newcontigs = new ArrayList(); - for (ContigInterval c : sp1) - newcontigs.add(c); - if (sp2 != null) - for (ContigInterval c : sp2) - newcontigs.add(c); - ArrayList newcis2 = new ArrayList(); - newcis2.addAll(newcis); - - ArrayList bestOrientation = new ArrayList(); - - for (int run = 0; run < numRuns; ++run) { - Collections.shuffle(newcontigs); - for (ContigInterval c : newcontigs) { - c.setOrientation((int)(2.0 * Math.random()) - 1); - } - boolean foundBetter = true; - - while (foundBetter) { - foundBetter = false; - for (ContigInterval c : newcontigs) { - if (calculateBest(newcis2, c, false) > 0) - foundBetter = true; - } - } - int score = calculateScore(newcis2, false) + calculateNonMarkerScore(newcis2); - if (score > bestScore) { - bestScore = score; - - newcis.clear(); - newcis.addAll(newcis2); - - bestOrientation.clear(); - for (ContigInterval c : newcis) - bestOrientation.add(c.getOrientation()); - } - } - int oi = 0; - for (ContigInterval c : newcis) - c.setOrientation(bestOrientation.get(oi++)); - } - - int score = calculateScore(newcis, false) + calculateNonMarkerScore(newcis); - //System.err.println(score); - if (score >= minImprovement + scoreOld + 2 * (splitSize1 + splitSize2 - 2)) { // at least +minImprovement in score and +2 for each cut... - System.err.println("split into " + splitSize1 + (splitSize2 <= 1 ? "" : "+" + splitSize2)); - System.err.print("number of markers"); - for (ContigInterval c : sp1) - System.err.print("\t" + c.getNumMarkers()); - if (sp2 != null) { - System.err.print("\t|"); - for (ContigInterval c : sp2) - System.err.print("\t" + c.getNumMarkers()); - } - System.err.println(); - - if (!printAnimation.equals("")) { - if (animation == 0) { - String fn = printAnimation + animation + ".la"; - - ArrayList orderSupport = calculateSupport(intervalsInChr); - try { - PrintStream ps = new PrintStream(fn); - printAnchoring(ps, intervalsInChr, orderSupport); - ps.close(); - } - catch (Exception ex){ - ex.printStackTrace(); - } - } - ++animation; - String fn = printAnimation + animation + ".la"; - ArrayList orderSupport = calculateSupport(newcis); - try { - PrintStream ps = new PrintStream(fn); - printAnchoring(ps, newcis, orderSupport); - ps.close(); - } - catch (Exception ex){ - ex.printStackTrace(); - } - } - - System.err.println("Score improvement = " + (score - scoreOld) + " score = " + score); - //System.err.println("score = " + score); - intervalsInChr.clear(); - intervalsInChr.addAll(newcis); - foundFix = true; - - break out; // found fix... - } - // set orientations back to stored state - for (int ci = 0; ci < orientation.length; ++ci) - intervalsInChr.get(ci).setOrientation((orientation[ci])); - } // fixIteration - } // for (int inr e=0; ... - } // while foundFix - int score = calculateScore(intervalsInChr, false); - System.out.println("#final score with corrected contigs " + score + " " + calculateNonMarkerScore(intervalsInChr)); - ArrayList orderSupport = calculateSupport(intervalsInChr); - - printAnchoring(intervalsInChr, orderSupport); - - //print bed for another run of Lep-Anchor - ContigInterval start = null; - for (int ci = 0; ci < intervalsInChr.size(); ++ci) { - ContigInterval c = intervalsInChr.get(ci); - ContigInterval next = ((ci + 1 < intervalsInChr.size()) ? intervalsInChr.get(ci + 1) : null); - boolean linked = false; - if (next != null) { - String key = calculateKey(c, next); - if (scaffoldingLink.containsKey(key) && scaffoldingLink.get(key) > 0) - linked = true; - } - if (start == null) - start = c; - - if (!linked) { - long pos1[] = c.getStartI(); - long pos2[] = c.getEndI(); - if (c != start) // more than one linked contigsIntervals... - if (c.getOrientation() >= 0) { - pos1 = start.getStartI(); - } else { - pos2 = start.getEndI(); - } - System.err.println(c.getContig() + "\t" + pos1[0] + "-" + pos1[1] + "\t" + pos2[0]+ "-" + pos2[1] + ((pos2.length==2) ? "" : "*") + "\t?\t" + c.getChromosome() + "\tbed"); - - start = null; - } - } - } - - private static void usageInfo() - { - System.err.println("usage: java PlaceAndOrientContigs bed=bed.file map=map1.txt [map2 [map3 ...]] options"); - System.err.println(" bed=file a file containing (contig start stop) intervals in 1-based coordinates"); - System.err.println(" map=file1 [file2 [...]] linkage map file(s)"); - System.err.println(" columns contig, pos, chromosome, map_bin_start [map_bin_stop [map_bin_start2 map_bin_stop2] [...]]"); - System.err.println(" orientation=+/- [+/- [...]] manual orientation for each map file (found automatically by default)"); - System.err.println(" chain=file chain file "); - System.err.println(" noChromosome=1 input file does not have chromosome column "); - System.err.println(" noIntervals=1 input file does not have intervals but map positions"); - System.err.println(" numRuns=NUM run this many runs to find better anchoring [5]"); - System.err.println(" chromosome=NUM take only chromosome NUM from the bed (chr=column 5) [not set]"); - System.err.println(" and from the map(s)"); - System.err.println(" numThreads=NUM number of threads [1]"); - - System.err.println(" compressMap=0 Do not compress map positions [1]"); - - System.err.println(" randomOrder=1 Start with a random anchoring [not set]"); - - System.err.println(" keepEmptyIntervals=1 Keep (contig)intervals without any markers [not set]"); - - System.err.println(" numErrors=NUM List at most this many potential errors [40]"); - System.err.println(" numErrorsPerContig=NUM List at most this many potential errors from one contig [3]\n"); - - System.err.println(" paf=file load alignment file in paf (minimap2) format"); - System.err.println(" maxBridge=NUM maximum scaffolding bridge length (for paf input) [50000]"); - System.err.println(" scalePaf=NUM multiply scaffolding links by NUM [1]"); - System.err.println(" maxIntersect=NUM maximum alignment intersection from paf [2000]"); - System.err.println(" maxPafScore=NUM maximum link score from the paf [not set]\n"); - - System.err.println(" scaleScore=NUM scale aligment scores to markers [0.00001] (0.00001 = 1kb 100% identity = 1)"); - System.err.println(" orientationPenalty=NUM if an aligment is in wrong orientation, multiply score by this [0.5]"); - System.err.println(" cutPenalty=NUM alignment cut penalty [0.001] (0.001 = 1kb gap = -1"); - - System.err.println(" useChainAndPaf=0 do not use both chain and paf score between contigs when available [not set]"); - - System.err.println(" proximity=file NUM1 NUM2 NUM3 load proximity data, NUM1=bin size [10000]"); - System.err.println(" NUM2=max distance in bins[25], NUM3=scale score [1.0]"); - - - System.err.println(" minHaplotypeAlignmentScore=NUM min alignment score required to consider haplotype [-10]"); - System.err.println(" minLinkAlignmentScore=NUM min alignment score required to consider contig link [-10]\n"); - - System.err.println(" evaluateAnchoring=FILE load initial anchring from a FILE (experimental)"); - System.err.println(" improveAnchoring=1 improve loaded initial anchring (experimental)"); - - System.err.println(" cutSites=FILE list possible contig cut sites for contigs"); - System.err.println(" the first cut site within each cut region is taken"); - - System.err.println(" findContigErrors=1 Iteratively find possible contig errors based on the map only"); - System.err.println(" paf, proximity and keepEmptyIntervals not allowed"); - - System.err.println(" minImprovement=NUM minimum improvement for findContigErrors [1]"); - System.err.println(" improvement of (NUM + 2*number_of_cuts) required"); - - System.err.println(" printAnimation=file print iterative solutions of findContigErrors to file0.la,...,fileN.la"); - - System.err.println(" alternativeJoins=1 prints alternative scaffolding joins, does not consider map information (yet)"); - System.err.println(" linksWithin=FILE only keep links between contig pairs listed in FILE"); - - } - - public static void main(String[] args) - { - - if (args.length == 0) { - usageInfo(); - System.exit(0); - } - String extraParameters = ""; - for (int i = 0; i < args.length; ++i) { - extraParameters += " " + args[i]; - } - ParameterParser pp = new ParameterParser(); - if (!pp.init(extraParameters)) { - usageInfo(); - System.exit(0); - } - pp.warning(new String[]{"findContigErrors","maxPafScore", "map", "bed", "orientation", "chain", "noIntervals", "noChromosome", "randomOrder", "paf", "evaluateAnchoring", "numRuns", "numErrors", "numErrorsPerContig", "scaleScore", "orientationPenalty", "cutPenalty", "maxBridge", "scalePaf", "minLinkAlignmentScore","minHaplotypeAlignmentScore", "maxIntersect", "chromosome", "keepEmptyIntervals", "cutSites", "useChainAndPaf", "proximity", "printAnimation", "compressMap", "minImprovement", "improveAnchoring", "alternativeJoins", "numThreads", "linksWithin"}); - -// ArrayList m = InputData.loadMap(pp.getValueAsString("map", null)); - //PlaceAndOrientContigs.solve(m); - - PlaceAndOrientContigs poc = new PlaceAndOrientContigs(); - - System.out.println("#java PlaceAndOrientContigs" + extraParameters); - - boolean findContigErrors = pp.getValueAsString("findContigErrors", "0").equals("1"); - - String chain = pp.getValueAsString("chain", null); - String paf = pp.getValueAsString("paf", null); - String prox = pp.getValueAsString("proximity", 0, null); - - if (findContigErrors && (paf != null || prox != null || pp.getValueAsString("keepEmptyIntervals", "0").equals("1"))) { - System.err.println("parameters paf, proximity and keepEmptyIntervals not allowed with findContigErrors!"); - System.exit(-1); - } - - poc.setNumThreads(Integer.parseInt(pp.getValueAsString("numThreads", "1"))); - poc.setScaleScore(Double.parseDouble(pp.getValueAsString("scaleScore", "0.00001"))); - - poc.setMaxIntersect(Integer.parseInt(pp.getValueAsString("maxIntersect", "2000"))); - - poc.setOrientationPenalty(Double.parseDouble(pp.getValueAsString("orientationPenalty", "0.5"))); - poc.setCutPenalty(Double.parseDouble(pp.getValueAsString("cutPenalty", "0.001"))); - poc.setMaxBridge(Integer.parseInt(pp.getValueAsString("maxBridge", "50000"))); - - int numMaps = pp.getNumberOfValues("map"); - - int chromosome = Integer.parseInt(pp.getValueAsString("chromosome", "-1")); - String bed = pp.getValueAsString("bed", null); - if (bed != null && numMaps > 0) - poc.loadBed(bed, chromosome); - else { - System.err.println("You have to provide bed and one or more map file(s)!"); - System.exit(-1); - } - - String cutSites = pp.getValueAsString("cutSites", null); - if (cutSites != null) { - poc.loadCutSites(cutSites); - } - - boolean findOrientation = false; - for (int i = 0; i < pp.getNumberOfValues("map"); ++i) { - String o = pp.getValueAsString("orientation", i, "?"); - if (!o.equals("+") && !o.equals("-")) - findOrientation = true; - } - if (pp.getNumberOfValues("map") != pp.getNumberOfValues("orientation") && pp.getNumberOfValues("orientation") > 0) { - System.err.println("You have to provide orientation for all maps or none!"); - System.exit(-1); - } - - poc.setCompressMap(pp.getValueAsString("compressMap", "1").equals("1")); - - for (int i = 0; i < pp.getNumberOfValues("map"); ++i) { - String o = pp.getValueAsString("orientation", i, "?"); - poc.addMap(pp.getValueAsString("map", i, null), !findOrientation && o.equals("-"), pp.getValueAsString("noChromosome", "0").equals("1"), pp.getValueAsString("noIntervals", "0").equals("1"), chromosome); - } - - if (chain != null) { - poc.loadChain(chain); - } - - int maxPafScore = Integer.parseInt(pp.getValueAsString("maxPafScore", "" + Integer.MAX_VALUE)); - - if (paf != null) - poc.loadPaf(paf, maxPafScore, Double.parseDouble(pp.getValueAsString("scalePaf", "1"))); - - if (prox != null) { - int bin = Integer.parseInt(pp.getValueAsString("proximity", 1, "10000")); - int maxD = Integer.parseInt(pp.getValueAsString("proximity", 2, "25")); - double scale = Double.parseDouble(pp.getValueAsString("proximity", 3, "1.0")); - poc.loadProximity(prox, bin, maxD, scale); - } - - poc.setNumRuns(Integer.parseInt(pp.getValueAsString("numRuns", "5"))); - - poc.setNumErrors(Integer.parseInt(pp.getValueAsString("numErrors", "40"))); - poc.setNumErrorsPerContig(Integer.parseInt(pp.getValueAsString("numErrorsPerContig", "3"))); - - poc.setMinHaplotypeAlignmentScore(Integer.parseInt(pp.getValueAsString("minHaplotypeAlignmentScore", "-10"))); - poc.setMinLinkAlignmentScore(Integer.parseInt(pp.getValueAsString("minLinkAlignmentScore", "-10"))); - - poc.setKeepEmptyIntervals(pp.getValueAsString("keepEmptyIntervals", "0").equals("1")); - poc.setUseChainAndPaf(pp.getValueAsString("useChainAndPaf", "1").equals("1")); - poc.setPrintAnimation(pp.getValueAsString("printAnimation", "")); - - String lwf = pp.getValueAsString("linksWithin", null); - if (lwf != null) - poc.linksWithin(lwf); - - poc.setCommentOutput(findContigErrors); - - String eval = pp.getValueAsString("evaluateAnchoring", null); - if (eval != null) { - poc.combineMaps(findOrientation, false, false); - ArrayList eval_result = InputData.loadLa(eval); - poc.evaluateScore(eval_result, pp.getValueAsString("improveAnchoring", "0").equals("1")); - } else { - poc.combineMaps(findOrientation, pp.getValueAsString("randomOrder", "0").equals("1"), true); - } - - if (findContigErrors) { - poc.setCommentOutput(false); - poc.findContigErrors(Integer.parseInt(pp.getValueAsString("minImprovement", "1"))); - } - - if (pp.getValueAsString("alternativeJoins", "0").equals("1")) { - poc.printAlternativeJoins(); - } - - - //System.out.println(m); - } - - private String[] contigs(String key) - { - String split[] = key.split("\t"); - int ip = split[2].indexOf('+'); - int im = split[2].indexOf('-'); - int p = Math.max(ip, im); - if (ip > 0 && im > 0) - p = Math.min(ip, im); - return new String[]{split[0], split[3].substring(p + 1)}; - - } - - private void linksWithin(String lwf) { - // TODO Clear proximity as well... - HashMap pairs = new HashMap(); - ArrayList> table = Input.loadTable(lwf, "\t "); - for (ArrayList row : table) { - if (row.size() >= 2) - pairs.put(row.get(0) + "\t" + row.get(1), 1); - } - - for (String key : chainLinkHash.keySet()) { - String cs[] = contigs(key); - String p1 = cs[0] + "\t" + cs[1]; - if (!pairs.containsKey(p1)) { - String p2 = cs[1] + "\t" + cs[0]; - if (!pairs.containsKey(p2)) - chainLinkHash.put(key, 0); - } - } - for (String key : scaffoldingLink.keySet()) { - String cs[] = contigs(key); - String p1 = cs[0] + "\t" + cs[1]; - if (!pairs.containsKey(p1)) { - String p2 = cs[1] + "\t" + cs[0]; - if (!pairs.containsKey(p2)) - scaffoldingLink.put(key, 0); - } - } - } - private int findLastIndex(ArrayList cis, int start, int maxDistance, boolean right) { - if (prox == null) - return start; - int n = cis.size(); - int end = start; - int d = prox.binLength(cis.get(start)); - if (right) - while (end + 1 < n && d < maxDistance) { - ++end; - d += prox.binLength(cis.get(end)); - } - else - while (end > 0 && d < maxDistance) { - --end; - d += prox.binLength(cis.get(end)); - } - - return end; - } - - //score between c and c + 1, c in 0,1,...,cis.size() - 2 - private int linkScore(ArrayList cis, int c) { - int ret = calculateChainScore(cis.get(c), cis.get(c + 1)); - if (prox != null) - ret += prox.linkScore(cis, c, c + 1); - return ret; - } - - //score of [start,end] => [start2,end2] in all orientations (++, -+, +-, --) - private int[] scores(ArrayList cis, int start, int end, int start2, int end2){ - ArrayList tmp1 = new ArrayList(); - for (int i = start; i <= end; ++i) - tmp1.add(cis.get(i)); - - ArrayList tmp2 = new ArrayList(); - for (int i = start2; i <= end2; ++i) - tmp2.add(cis.get(i)); - - int s[] = new int[4]; - for (int o2 = 0; o2 < 2; ++o2) { - for (int o1 = 0; o1 < 2; ++o1) { - ArrayList tmp = new ArrayList(); - tmp.addAll(tmp1); - tmp.addAll(tmp2); - s[o1 + 2 * o2] = linkScore(tmp, end - start); - - Collections.reverse(tmp1); - for (ContigInterval c : tmp1) - c.flipOrientation(); - } - Collections.reverse(tmp2); - for (ContigInterval c : tmp2) - c.flipOrientation(); - } - return s; - } - - private void printAlternativeJoins() { - int maxD = 0; - if (prox != null) - maxD = prox.getMaxDistance(); - int n = intervalsInChr.size(); - if (n <= 1) - return; - - int jTable[][] = new int[2 * n][2 * n]; - int start = 0; - while (start < n) { - int end_ = findLastIndex(intervalsInChr, start, maxD, true); // this could be faster... - for (int end = ((start == 0) ? 0 : end_); end <= end_; ++end) - for (int start2 = end + 1; start2 < n; ++start2) { - int end2 = findLastIndex(intervalsInChr, start2, maxD, true); // and this - int s[] = scores(intervalsInChr, start, end, start2, end2); - - //end2 is always maximal - boolean endMaximal = (end == end_); - boolean startMaximal = (findLastIndex(intervalsInChr, end, maxD, false) == start); - boolean start2Maximal = (findLastIndex(intervalsInChr, end2, maxD, false) == start2); - //remove non-maximal scores, otherwise you count each score multiple times for same endpoint with other end not maximal... - - if (startMaximal) - jTable[2 * end] [2 * start2] += s[0]; // += is not needed, = is enough - if (endMaximal) - jTable[2 * start + 1][2 * start2] += s[1]; - if (startMaximal && start2Maximal) - jTable[2 * end] [2 * end2 + 1] += s[2]; - if (endMaximal && start2Maximal) - jTable[2 * start + 1][2 * end2 + 1] += s[3]; - - int s2[] = scores(intervalsInChr, start2, end2, start, end); - - if (start2Maximal && endMaximal) - jTable[2 * end2] [2 * start] += s2[0]; - if (endMaximal) - jTable[2 * start2 + 1][2 * start] += s2[1]; - if (start2Maximal && startMaximal) - jTable[2 * end2] [2 * end + 1] += s2[2]; - if (startMaximal) - jTable[2 * start2 + 1][2 * end + 1] += s2[3]; - } - ++start; - } -/* for (int i = 0; i < n + n; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < n + n; ++j) { - if (j != 0) - sb.append('\t'); - if (i == j) - sb.append('X'); - else - sb.append(jTable[i][j]); - } - System.err.println(sb); - }*/ - System.err.println("*** alternativeJoins ***"); - for (int ci = 0; ci < n; ++ci) { - //ArrayList list = new ArrayList(); - for (int i = 0; i < n + n; ++i) { - int ip2 = i >> 1; - if (jTable[2 * ci][i] > 0) { - System.err.println(intervalsInChr.get(ci) + "\t+\t" + intervalsInChr.get(ip2) + "\t" + ("+-".substring(i & 1, (i & 1) + 1)) + "\t" + jTable[2 * ci][i] + "\t" + ci + "\t" + ip2); - } - if (jTable[2 * ci + 1][i] > 0) { - System.err.println(intervalsInChr.get(ci) + "\t-\t" + intervalsInChr.get(ip2) + "\t" + ("+-".substring(i & 1, (i & 1) + 1)) + "\t" + jTable[2 * ci + 1][i] + "\t" + ci + "\t" + ip2); - } - } - - } - } -} diff --git a/software/LepAnchor/src/Proximity.java b/software/LepAnchor/src/Proximity.java deleted file mode 100644 index f1fa1e0..0000000 --- a/software/LepAnchor/src/Proximity.java +++ /dev/null @@ -1,657 +0,0 @@ -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; - - -public class Proximity { - private int binSize; - private int maxDistance; - private double scale; - //eg. binSize=10000 and maxDistance=25 =>10kb bins and max of 250kb - - //TODO: find possible assembly errors... - - public Proximity(int binSize, int maxDistance, double scale){ - this.binSize = binSize; - this.maxDistance = maxDistance; - this.scale = scale; - } - HashMap distanceHash = new HashMap(); - - public int getMaxDistance(){ - return maxDistance; - } - - public boolean loadData(String fn, HashMap> bedHash) - { - boolean nonEmpty = false; - System.err.println("loading proximity data..."); - HashMap symmetryHash = new HashMap(); - try { - HashMap distanceHash_tmp = new HashMap(); - - BufferedReader br = null; - if (fn.equals("-")) - br = new BufferedReader(new InputStreamReader(System.in)); - else - br = new BufferedReader(new FileReader(fn)); - do { - ArrayList row = Input.loadTableRow(br, "\t "); - if (row == null) - break; - String contig1 = row.get(0); - long pos1 = Long.parseLong(row.get(1)); - if (bedHash.containsKey(contig1)) - for (ContigInterval ci1 : bedHash.get(contig1)) - if (ci1.inside(pos1)) { - - int p1 = bin(pos1); - - int de1 = bin(ci1.getEnd()) - p1; - int ds1 = p1 - bin(ci1.getStart()); - - if (de1 >= maxDistance && ds1 >= maxDistance) - continue; - - String contig2 = row.get(2); - long pos2 = Long.parseLong(row.get(3)); - if (bedHash.containsKey(contig2)) - for (ContigInterval ci2 : bedHash.get(contig2)) - if (ci1 != ci2 && ci2.inside(pos2)) { - //DO MAGIC HERE - - int p2 = bin(pos2); - int de2 = bin(ci2.getEnd()) - p2; - int ds2 = p2 - bin(ci2.getStart()); - - if (de2 >= maxDistance && ds2 >= maxDistance || Math.min(de1, ds1) + Math.min(de2, ds2) >= maxDistance) - continue; - - nonEmpty = true; - - double value = Double.parseDouble(row.get(4)) * scale; - - if (de1 + ds2 < maxDistance) { // ++ - String key = calculateKey(ci1, true, ci2, true); - symmetryHash.put(key, calculateKey(ci2, false, ci1, false)); - - double list[] = distanceHash_tmp.get(key); - if (list == null) { - list = new double[maxDistance]; - distanceHash_tmp.put(key, list); - } - list[de1 + ds2] += value; - } - if (de1 + de2 < maxDistance) { // +- - String key = calculateKey(ci1, true, ci2, false); - symmetryHash.put(key, calculateKey(ci2, true, ci1, false)); - - double list[] = distanceHash_tmp.get(key); - if (list == null) { - list = new double[maxDistance]; - distanceHash_tmp.put(key, list); - } - list[de1 + de2] += value; - } - if (ds1 + ds2 < maxDistance) { // -+ - String key = calculateKey(ci1, false, ci2, true); - symmetryHash.put(key, calculateKey(ci2, false, ci1, true)); - - double list[] = distanceHash_tmp.get(key); - if (list == null) { - list = new double[maxDistance]; - distanceHash_tmp.put(key, list); - } - list[ds1 + ds2] += value; - } - if (ds1 + de2 < maxDistance) { // -- - String key = calculateKey(ci1, false, ci2, false); - symmetryHash.put(key, calculateKey(ci2, true, ci1, true)); - - double list[] = distanceHash_tmp.get(key); - if (list == null) { - list = new double[maxDistance]; - distanceHash_tmp.put(key, list); - } - list[ds1 + de2] += value; - } - } - } - } while (true); - br.close(); - - //transfer and truncate data from distanceHash_tmp (double) to distanceHash (int) - //make scores symmetric and do rounding - ArrayList missingKeys = new ArrayList(); - for (String key : distanceHash_tmp.keySet()) { - String sk = symmetryHash.get(key); - if (!distanceHash_tmp.containsKey(sk)) - missingKeys.add(key); - } - for (String key : missingKeys) { // put missing symmetric keys to the hashes - String sk = symmetryHash.get(key); - distanceHash_tmp.put(sk, distanceHash_tmp.get(key)); - symmetryHash.put(sk, key); - } - - for (String key : distanceHash_tmp.keySet()) { - double list[] = distanceHash_tmp.get(key); - double list2[] = distanceHash_tmp.get(symmetryHash.get(key)); // symmetric key - - int new_list[] = new int[maxDistance]; - - double sum = 0.0; - for (int i = 0; i < maxDistance; ++i) { - sum += list[i] + list2[i]; - new_list[maxDistance - i - 1] = (int)(0.5 * sum + 0.5); - } - int maxLength = maxDistance; - while (maxLength > 0 && new_list[maxLength - 1] == 0) - --maxLength; - if (maxLength > 0) - distanceHash.put(key, Arrays.copyOf(new_list, maxLength)); - -/* int maxIndex = maxDistance - 1; - double sum = 0.0; - while (maxIndex >= 0 && sum + list[maxIndex] + list2[maxIndex] < 1.0) { - sum += list[maxIndex] + list2[maxIndex]; - --maxIndex; - } - if (maxIndex >= 0) { - int new_list[] = new int[maxIndex + 1]; - while (maxIndex >= 0) { - sum += list[maxIndex] + list2[maxIndex]; - new_list[maxIndex] = (int)(0.5 * sum + 0.5); - --maxIndex; - } - distanceHash.put(key, new_list); - }*/ - } - - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Error in file " + fn); - } - - System.err.println("Proximity links:"); - for (String key: distanceHash.keySet()) - System.err.println(key + "\t" + distanceHash.get(key)[0]); - return nonEmpty; - } - - - - //"bin" a contig position - private int bin(long position) - { - return (int) ((position - 1) / binSize); - } - - //how many bins a contig spans... - public int binLength(ContigInterval ci) - { - int s = bin(ci.getStart()); - int e = bin(ci.getEnd()); - return (e - s + 1); - } - - private String calculateKey(ContigInterval c1, ContigInterval c2) - { - return calculateKey(c1, c1.getOrientation() >= 0, c2, c2.getOrientation() >= 0); - } - - private String calculateKey(ContigInterval c1, boolean orientation1, ContigInterval c2, boolean orientation2) - { - return c1.toString() + (orientation1 ? '+':'-') + c2.toString() + (orientation2 ? '+':'-'); - } - - // calculate a list of contigs that should be evaluate against cis.get(ci) - private ArrayList next(ArrayList cis, int ci, int last) { - ArrayList ret = new ArrayList(); - //int n = cis.size(); - int i = ci + 1; - int d = 0; - while (i <= last && d < maxDistance) { - ContigInterval c = cis.get(i); - ret.add(i); - d += binLength(c); - ++i; - } - return ret; - } - - // calculate a list of contigs that should be evaluate against cis.get(ci) - private ArrayList prev(ArrayList cis, int ci, int first) { - ArrayList ret = new ArrayList(); - int i = ci - 1; - int d = 0; - while (i >= first && d < maxDistance) { - ContigInterval c = cis.get(i); - ret.add(i); - d += binLength(c); - --i; - } - return ret; - } - - private int borderScore(ArrayList cis, ArrayList prev, ArrayList next){ - int ret = 0; - int dp = 0; - for (int pci : prev) { - ContigInterval pc = ((pci < 0) ? cis.get(-pci - 1) : cis.get(pci)); - int dnp = dp; - dp += binLength(pc); - for (int nci : next) { - if (dnp >= maxDistance) - break; - ContigInterval nc = ((nci < 0) ? cis.get(-nci - 1) : cis.get(nci)); - - ret += score(pc, pci < 0, nc, nci < 0, dnp); - dnp += binLength(nc); - } - } - return ret; - } - // difference in score if cis[start...end] are moved to new_position and possibly flipped - public int scoreChange_slow(ArrayList cis, int start, int end, int moveDirection, boolean flip) - { - int scoreOld = score(cis); - PlaceAndOrientContigs.changeOrder(cis, start, end, moveDirection, flip); - int scoreNew = score(cis); - PlaceAndOrientContigs.changeOrderReverse(cis, start, end, moveDirection, flip); - //System.err.println(scoreNew - scoreOld); - return scoreNew - scoreOld; - } - - // difference in score if cis[start...end] are moved to new_position and possibly flipped - public int scoreChange(ArrayList cis, int start, int end, int moveDirection, boolean flip) - { - int ret = 0; - int n = cis.size(); - if (moveDirection == 0) { // flip - if (flip) { - //first part - ArrayList next = next(cis, start - 1, end); // start, start+1, ..., end - ArrayList prev = prev(cis, start, 0); // start-1, start-2, ..., 0 - - ArrayList new_next = new ArrayList(); - int d = 0; - for (int i = end; d < maxDistance && i >= start; --i) { - ContigInterval nc = cis.get(i); - new_next.add(-i - 1); - d += binLength(nc); - } - //subtract old score for start-1 -> start boundary, - ret -= borderScore(cis, prev, next); - //...and add new score for this boundary - ret += borderScore(cis, prev, new_next); - - //second part - - next = next(cis, end, n - 1); // end+1, end+2, ..., n-1 - prev = prev(cis, end + 1, start); // end, end-1, ..., start - - ArrayList new_prev = new ArrayList(); - d = 0; - for (int i = start; d < maxDistance && i <= end; ++i) { - ContigInterval nc = cis.get(i); - new_prev.add(-i - 1); - d += binLength(nc); - } - //subtract old score for end -> end+1 boundary, - ret -= borderScore(cis, prev, next); - //..and add new score for this boundary - ret += borderScore(cis, new_prev, next); - - } // else nothing to do... - } else { // move one or multiple contigs... - if (moveDirection >= 0) { // actually moveDirection > 0 - //int first = start - //int second = end; - //int third = end + moveDirection; - //first part - ArrayList next = next(cis, start - 1, n - 1); // start, start+1, ..., n-1 - ArrayList prev = prev(cis, start, 0); // start-1, start-2, ..., 0 - ret -= borderScore(cis, prev, next); - - ArrayList new_next = new ArrayList(); - int d = 0; - - for (int i = end + 1; d < maxDistance && i <= end + moveDirection; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - - if (flip) - for (int i = end; d < maxDistance && i >= start; --i) { - ContigInterval nc = cis.get(i); - new_next.add(-i - 1); - d += binLength(nc); - } - else - for (int i = start; d < maxDistance && i <= end; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - - for (int i = end + moveDirection + 1; d < maxDistance && i < n; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - //flip: new_next = end + 1, ..., end + moveDirection,end...start, end + moveDirection + 1 ... n - 1 - //!flip: new_next = end + 1, ..., end + moveDirection,start...end, end + moveDirection + 1 ... n - 1 - //prev = start - 1, start - 2, ..., 0 - - ret += borderScore(cis, prev, new_next); - - //second part - next = next(cis, end, n - 1); // second, second+1, ..., n-1 - prev = prev(cis, end + 1, start); // second-1, second-2, ..., first - - ret -= borderScore(cis, prev, next); - - new_next = new ArrayList(); - d = 0; - if (flip) - for (int i = end; d < maxDistance && i >= start; --i) { - ContigInterval nc = cis.get(i); - new_next.add(-i - 1); - d += binLength(nc); - } - else - for (int i = start; d < maxDistance && i <= end; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - for (int i = end + moveDirection + 1; d < maxDistance && i < n; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - //flip: new_next = end...start, end + moveDirection + 1 ... n - 1 - //!flip: new_next = start..end, ..., end + moveDirection + 1 ... n - 1 - - ArrayList new_prev = new ArrayList(); - d = 0; - - for (int i = end + moveDirection; d < maxDistance && i > end; --i) { //TODO: check if (>= end or) > is proper - ContigInterval nc = cis.get(i); - new_prev.add(i); - d += binLength(nc); - } - //new_prev = end + moveDirection...end - ret += borderScore(cis, new_prev, new_next); - - //third part - next = next(cis, end + moveDirection, n - 1); // third, third+1, ..., n-1 - prev = prev(cis, end + moveDirection + 1, end); // third-1, third-2, ..., second - - ret -= borderScore(cis, prev, next); - new_prev = new ArrayList(); - - //TODO: calculate new_prev - d = 0; - if (flip) - for (int i = start; d < maxDistance && i <= end; ++i) { - ContigInterval nc = cis.get(i); - new_prev.add(-i - 1); - d += binLength(nc); - } - else - for (int i = end; d < maxDistance && i >= start; --i) { - ContigInterval nc = cis.get(i); - new_prev.add(i); - d += binLength(nc); - } - ret += borderScore(cis, new_prev, next); - //System.err.println(borderScore(cis, prev, next)); - //System.err.println(prev); - //System.err.println(next); - - } else { //moveDirection < 0 - //int first = start + moveDirection; - //int second = start; - //int third = end; - //first part - ArrayList next = next(cis, start + moveDirection - 1, n - 1); // first, first+1, ..., n-1 - ArrayList prev = prev(cis, start + moveDirection, 0); // first-1, first-2, ..., 0 - ret -= borderScore(cis, prev, next); - - ArrayList new_next = new ArrayList(); - int d = 0; - if (flip) - for (int i = end; d < maxDistance && i >= start; --i) { - ContigInterval nc = cis.get(i); - new_next.add(-i - 1); - d += binLength(nc); - } - else - for (int i = start; d < maxDistance && i <= end; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - - for (int i = start + moveDirection; d < maxDistance && i < start; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - - for (int i = end + 1; d < maxDistance && i < n; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - - ret += borderScore(cis, prev, new_next); - //System.err.println(""); - //System.err.println(prev); - //System.err.println(next); - //System.err.println("->"); - //System.err.println(prev); - //System.err.println(new_next); - - //second part - next = next(cis, start - 1, n - 1); // second, second+1, ..., n-1 - prev = prev(cis, start, start + moveDirection); // second-1, second-2, ..., first - - ret -= borderScore(cis, prev, next); - - new_next = new ArrayList(); - - d = 0; - for (int i = start + moveDirection; d < maxDistance && i < start; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - - for (int i = end + 1; d < maxDistance && i < n; ++i) { - ContigInterval nc = cis.get(i); - new_next.add(i); - d += binLength(nc); - } - - ArrayList new_prev = new ArrayList(); - - d = 0; - if (flip) - for (int i = start; d < maxDistance && i <= end; ++i) { - ContigInterval nc = cis.get(i); - new_prev.add(-i - 1); - d += binLength(nc); - } - else - for (int i = end; d < maxDistance && i >= start; --i) { - ContigInterval nc = cis.get(i); - new_prev.add(i); - d += binLength(nc); - } - - ret += borderScore(cis, new_prev, new_next); - //System.err.println(""); - //System.err.println(prev); - //System.err.println(next); - //System.err.println("->"); - //System.err.println(new_prev); - //System.err.println(new_next); - - //third part - next = next(cis, end, n - 1); // third, third+1, ..., n-1 - prev = prev(cis, end + 1, start); // third-1, third-2, ..., second - - ret -= borderScore(cis, prev, next); - new_prev = new ArrayList(); - //TODO: calculate new_prev - d = 0; - for (int i = start - 1; d < maxDistance && i >= start + moveDirection; --i) { - ContigInterval nc = cis.get(i); - new_prev.add(i); - d += binLength(nc); - } - ret += borderScore(cis, new_prev, next); - //System.err.println(""); - //System.err.println(prev); - //System.err.println(next); - //System.err.println("->"); - //System.err.println(new_prev); - //System.err.println(next); - } - - } - //if (true) { - // int value = scoreChange_slow(cis, start, end, moveDirection, flip); - // if (value != ret) - // System.err.println(ret + "!=" + value + " " + start + " " + end + " " + moveDirection + " " + flip); - // return value; - //} - - return ret; - } - - public int score(ArrayList cis) - { - int ret = 0; - for (int i = 1; i < cis.size(); ++i) { - int j = i - 1; - int d = 0; - while (j >= 0 && d < maxDistance) { - ret += score(cis.get(j), cis.get(i), d); - d += binLength(cis.get(j)); - --j; - } - } - return ret; - } - - private int score(ContigInterval c1, ContigInterval c2, int distance) - { - int d[] = distanceHash.get(calculateKey(c1, c2)); - if (d != null && d.length > distance) - return d[distance]; - return 0; - } - - public int score(String key, int distance) - { - int d[] = distanceHash.get(key); - if (d != null && d.length > distance) - return d[distance]; - return 0; - } - - private int score(ContigInterval c1, boolean flip1, ContigInterval c2, boolean flip2, int distance) - { - int d[] = distanceHash.get(calculateKey(c1, (c1.getOrientation() >= 0) ^ flip1, c2, (c2.getOrientation() >= 0) ^ flip2)); - if (d != null && d.length > distance) - return d[distance]; - return 0; - } - - public static void main(String args[]) - { - //Test Proximity class - ArrayList bed = InputData.loadBed("proximity.bed"); - - HashMap> bedHash2 = new HashMap>(); - for (ContigInterval ci : bed) { - String contig = ci.getContig(); - if (!bedHash2.containsKey(contig)) - bedHash2.put(contig, new ArrayList()); - bedHash2.get(contig).add(ci); - } - Proximity p = new Proximity(10000, 2000, 0.01); - p.loadData("ld.txt", bedHash2); - ContigInterval c1 = bedHash2.get("000604F|quiver|pilon").get(0); - ContigInterval c2 = bedHash2.get("000000F|quiver|pilon").get(0); - ContigInterval c3 = bedHash2.get("000310F|quiver|pilon").get(0); - ArrayList cis = new ArrayList(); - cis.add(c1); - cis.add(c2); - cis.add(c3); - Collections.reverse(cis); - for (int orient = 0; orient < 8; ++orient) { - c1.setOrientation((orient & 4) - 1); - c2.setOrientation((orient & 2) - 1); - c3.setOrientation((orient & 1) - 1); - System.err.println(Integer.toBinaryString(orient + 8).substring(1) + ":\t" + p.score(cis)); - } - System.err.println(); - Collections.reverse(cis); - for (int orient = 0; orient < 8; ++orient) { - c1.setOrientation((orient & 4) - 1); - c2.setOrientation((orient & 2) - 1); - c3.setOrientation((orient & 1) - 1); - System.err.println(Integer.toBinaryString(orient + 8).substring(1) + ":\t" + p.score(cis)); - } - - c1.setOrientation(1); - c2.setOrientation(-1); - c3.setOrientation(1); - System.err.println(p.score(cis)); - System.err.println(); - for (int i = 0; i < 3; ++i) { - System.err.println(p.scoreChange(cis, i, i, 0, true)); - System.err.println(p.scoreChange_slow(cis, i, i, 0, true)); - System.err.println(); - } - - for (int i = 0; i < 2; ++i) { - System.err.println(p.scoreChange(cis, i, i, 1, true)); - System.err.println(p.scoreChange_slow(cis, i, i, 1, true)); - System.err.println(); - } - System.err.println(p.scoreChange(cis, 0, 0, 2, true)); - System.err.println(p.scoreChange_slow(cis, 0, 0, 2, true)); - System.err.println(); - - System.err.println(p.scoreChange(cis, 1, 2, -1, true)); - System.err.println(p.scoreChange_slow(cis, 1, 2, -1, true)); - System.err.println(); - - - System.err.println(p.scoreChange(cis, 0, 1, 1, true)); - System.err.println(p.scoreChange_slow(cis, 0, 1, 1, true)); - System.err.println(); - - } - // linkScore between ... ,prevC-1, prevC and nextC, nextC + 1, ... - public int linkScore(ArrayList cis, int prevC, int nextC) { - ArrayList next = next(cis, nextC - 1, cis.size() - 1); - ArrayList prev = prev(cis, prevC + 1, 0); - return borderScore(cis, prev, next); - } - - public boolean linkedIntervals(ArrayList cis, int c1, int c2) { - return (linkScore(cis, c1, c2) > 0); - } -} diff --git a/software/LepMap3/scripts/affx2post.awk b/software/LepMap3/scripts/affx2post.awk deleted file mode 100644 index 9ca5d4a..0000000 --- a/software/LepMap3/scripts/affx2post.awk +++ /dev/null @@ -1,83 +0,0 @@ -#converts a genotype file (SNP_name + AA/AB/BB for each individual) to posterior -#awk [-verror=0.001] -f affx2post.awk genotypes.txt >genotypes.post -function alleles2Index(a, b) -{ - if (b == 1) - return a - if (b == 2) - return 3 + a - if (b == 3) - return 5 + a - return 10 -} -function allele1(code) -{ - if (code <= 4) - return 1 - if (code <= 7) - return 2 - if (code <= 9) - return 3 - return 4 -} -function allele2(code) -{ - if (code <= 4) - return code - if (code <= 7) - return code - 3 - if (code <= 9) - return code - 5 - return 4 -} - -function distance(code1, code2 ,a1,a2,b1,b2) -{ - if (code1 == code2) - return 0 - a1 = allele1(code1) - a2 = allele2(code1) - - b1 = allele1(code2) - b2 = allele2(code2) - - if (a1 == b1 || a1 == b2 || a2 == b1 || a2 == b2) - return 1 - return 2 -} - - - -BEGIN{ - FS="\t" - OFS="\t" - if (error == "") - error = 0.001 - - code = 1 - for (i = 1; i <= 4; ++i) - for (j = i; j <= 4; ++j) { - s = "" - for (k = 1; k <= 10; ++k) - s = s " " error ^ distance(code, k) - map[i " " j] = substr(s, 2) - map[j " " i] = map[i " " j] - ++code - } - - map["0 0"] = "1 1 1 1 1 1 1 1 1 1" - map2["AA"]="1 1" - map2["AB"]="1 2" - map2["BB"]="2 2" - map2["NoCall"]="0 0" -} - -($1 !~ /^#/) { - $1=$1"\t"$1 - if (++line > 1) - for (i = 2; i <= NF; ++i) - $i = map[map2[$i]] - print -} - - diff --git a/software/LepMap3/scripts/allPaternal.awk b/software/LepMap3/scripts/allPaternal.awk deleted file mode 100644 index 5226eaf..0000000 --- a/software/LepMap3/scripts/allPaternal.awk +++ /dev/null @@ -1,45 +0,0 @@ -#flips maternally informative markers to paternally inf -BEGIN{ - FS="\t" - OFS="\t" - inf["0 1.0 0 0 0 0 0 0 0 0"]=1 - inf["0 0 1.0 0 0 0 0 0 0 0"]=1 - inf["0 0 0 1.0 0 0 0 0 0 0"]=1 - inf["0 0 0 0 0 1.0 0 0 0 0"]=1 - inf["0 0 0 0 0 0 1.0 0 0 0"]=1 - inf["0 0 0 0 0 0 0 0 1.0 0"]=1 - - inf["0 1 0 0 0 0 0 0 0 0"]=1 - inf["0 0 1 0 0 0 0 0 0 0"]=1 - inf["0 0 0 1 0 0 0 0 0 0"]=1 - inf["0 0 0 0 0 1 0 0 0 0"]=1 - inf["0 0 0 0 0 0 1 0 0 0"]=1 - inf["0 0 0 0 0 0 0 0 1 0"]=1 - -} - -(NR<=7){print} - -(NR==2){for (i = 3; i<=NF; ++i) {if (!($i in d)) d[$i] = ++p; f[i]=d[$i]}} - -(NR==4){for (i = 3; i<=NF; ++i) if ($i==0) pa[f[i], ++count[f[i]]]=i} - -(NR==6){for (i = 3; i<=NF; ++i) sex[i]=$i} - -#(NR==7){print "#"} - -(NR>7){ - for (j = 1; j <= p; ++j) { - i = 0 - if (inf[$(pa[j, 1])]==1) - i += sex[pa[j, 1]] - if (inf[$(pa[j, 2])]==1) - i += sex[pa[j, 2]] - if (i == 2) { - tmp = $(pa[j, 1]) - $(pa[j, 1]) = $(pa[j, 2]) - $(pa[j, 2]) = tmp - } - } - print -} diff --git a/software/LepMap3/scripts/genotypes2post.awk b/software/LepMap3/scripts/genotypes2post.awk deleted file mode 100644 index e0baf69..0000000 --- a/software/LepMap3/scripts/genotypes2post.awk +++ /dev/null @@ -1,81 +0,0 @@ -#converts a genotype file (transpose of linkage file with marker names) to posterior -#awk [-verror=0.001] -f genotypes2post.awk genotypes >genotypes.post -function alleles2Index(a, b) -{ - if (b == 1) - return a - if (b == 2) - return 3 + a - if (b == 3) - return 5 + a - return 10 -} -function allele1(code) -{ - if (code <= 4) - return 1 - if (code <= 7) - return 2 - if (code <= 9) - return 3 - return 4 -} -function allele2(code) -{ - if (code <= 4) - return code - if (code <= 7) - return code - 3 - if (code <= 9) - return code - 5 - return 4 -} - -function distance(code1, code2 ,a1,a2,b1,b2) -{ - if (code1 == code2) - return 0 - a1 = allele1(code1) - a2 = allele2(code1) - - b1 = allele1(code2) - b2 = allele2(code2) - - if (a1 == b1 || a1 == b2 || a2 == b1 || a2 == b2) - return 1 - return 2 -} - - - -BEGIN{ - FS="\t" - OFS="\t" - if (error == "") - error = 0.001 - - code = 1 - for (i = 1; i <= 4; ++i) - for (j = i; j <= 4; ++j) { - s = "" - for (k = 1; k <= 10; ++k) - s = s " " error ^ distance(code, k) - map[i " " j] = substr(s, 2) - map[j " " i] = map[i " " j] - map[j " " i] = map[i " " j] - map[i " " j] = map[i " " j] - ++code - } - - map["0 0"] = "1 1 1 1 1 1 1 1 1 1" - map["0 0"] = "1 1 1 1 1 1 1 1 1 1" -} - -($1 !~ /^#/) { - if (++line > 6) - for (i = 3; i <= NF; ++i) - $i = map[$i] - print -} - - diff --git a/software/LepMap3/scripts/loc2genotypes.awk b/software/LepMap3/scripts/loc2genotypes.awk deleted file mode 100644 index feca503..0000000 --- a/software/LepMap3/scripts/loc2genotypes.awk +++ /dev/null @@ -1,102 +0,0 @@ -#converts a loc file (JoinMap) to genotypes, that can be converted back post file -#should handle windows end-of-line characters as well -#awk -f locsingle.awk file.loc|awk -f loc2genotypes.awk|awk -f genotypes2post.awk |java -cp Lep-MAP3/bin ... data=- ... -BEGIN{ - map2["-"] = 0 - # - map2["l"] = 1 - map2["m"] = 2 - # - map2["n"] = 1 - map2["p"] = 2 - # - map2["h"] = 1 - map2["k"] = 2 - # - map2["e"] = 1 - map2["f"] = 2 - map2["g"] = 3 - # - map2["a"] = 1 - map2["b"] = 2 - map2["c"] = 3 - map2["d"] = 4 - for (i in map2) { - map[i] = map2[i] - map[toupper(i)] = map2[i] - } - delete map2 - markers[""] - markers[""] - markers[""] - markers[""] - markers[""] - markers[""] - markers[""] - markers[""] -} -($2 in markers) { - if ($NF == "\r") #windows end of line - --NF - ++line - start = 3 - if ($3 ~ /{.*}/ || $3 ~ /\(.*\)/) - start = 4 -# if (start == 3 && !($3 ~ /{.*}/) { -# print "Error: missing phase on data" >/dev/stderr -# exit(-1) -# } - - if (line == 1) { ## print pedigree - s = "CHR\tPOS" - for (i = start; i <= NF + 4; ++i) - s = s "\tF" - print s - s = "CHR\tPOS\tGP1\tGP2\tP1\tP2" - for (i = start; i <= NF; ++i) - s = s "\t" (i - start + 1) - print s - s = "CHR\tPOS\t0\t0\tGP1\tGP2" - for (i = start; i <= NF; ++i) - s = s "\tP1" - print s - s = "CHR\tPOS\t0\t0\t0\t0" - for (i = start; i <= NF; ++i) - s = s "\tP2" - print s - s = "CHR\tPOS\t1\t1\t1\t2" - for (i = start; i <= NF; ++i) - s = s "\t0" - print s - s = "CHR\tPOS" - for (i = start; i <= NF + 4; ++i) - s = s "\t0" - print s - } - s = $1 "\t" line - if (start == 4) { - if ($3 ~ /{0/) - s = s "\t" map[substr($2, 2, 1)] " " map[substr($2, 2, 1)] - else if ($3 ~ /{1/) - s = s "\t" map[substr($2, 3, 1)] " " map[substr($2, 3, 1)] - else - s = s "\t0 0" - - if ($3 ~ /{.0/) - s = s "\t" map[substr($2, 5, 1)] " " map[substr($2, 5, 1)] - else if ($3 ~ /{.1/) - s = s "\t" map[substr($2, 6, 1)] " " map[substr($2, 6, 1)] - else - s = s "\t0 0" - - } else - s = s "\t0 0\t0 0" - s = s "\t" map[substr($2, 2, 1)] " " map[substr($2, 3, 1)] "\t" map[substr($2, 5, 1)] " " map[substr($2, 6, 1)] - - for (i = start; i <= NF; ++i) - s = s "\t" map[substr($i, 1, 1)] " " map[substr($i, 2, 1)] - print s -} -END{ - -} diff --git a/software/LepMap3/scripts/locsingle.awk b/software/LepMap3/scripts/locsingle.awk deleted file mode 100644 index ac87d4f..0000000 --- a/software/LepMap3/scripts/locsingle.awk +++ /dev/null @@ -1,8 +0,0 @@ -#puts loc files on single line -{ - i = index($0, ";") - if (i > 0) - printf(substr($0, 1, i-1)) - else - print -} diff --git a/software/LepMap3/scripts/map2genotypes.awk b/software/LepMap3/scripts/map2genotypes.awk deleted file mode 100644 index a587300..0000000 --- a/software/LepMap3/scripts/map2genotypes.awk +++ /dev/null @@ -1,66 +0,0 @@ -#converts phased data to "genotypes" -#usage: -#java ... OrderMarkers2 ... outputPhasedData=1 > order_with_phase_LM3.txt -#awk [-vchr=X] [-vfullData=1] -f map2genotypes.awk order_with_phase_LM3.txt -#output columns marker name, chr, male postion, female postion, genotypes coded as "1 1", "1 2", "2 2" and 0 as missing -#providing fullData ouputs parents and pedigree... -BEGIN{ - map["00"]="1 1" - map["01"]="1 2" - map["10"]="2 1" - map["11"]="2 2" - map["0-"]="1 0" - map["-0"]="0 1" - map["-1"]="0 2" - map["1-"]="2 0" - map["--"]="0 0" - if (chr == "") - chr = 0 -} -(/^[^#]/){ - if (!notFirst && fullData){ - notFirst = 1 - s1 = "MARKER\tCHR\tMALE_POS\tFEMALE_POS" - s2 = "MARKER\tCHR\tMALE_POS\tFEMALE_POS" - s3 = "MARKER\tCHR\tMALE_POS\tFEMALE_POS" - s4 = "MARKER\tCHR\tMALE_POS\tFEMALE_POS" - s5 = "MARKER\tCHR\tMALE_POS\tFEMALE_POS" - s6 = "MARKER\tCHR\tMALE_POS\tFEMALE_POS" - for (i = 7; i<=NF; i+=3) { - n = length($i) / 2 - p1 = "P" (++numParents) - p2 = "P" (++numParents) - s1 = s1 "\t" p1 "x" p2 "\t" p1 "x" p2 - s2 = s2 "\t" p1 "\t" p2 - s3 = s3 "\t" 0 "\t" 0 - s4 = s4 "\t" 0 "\t" 0 - s5 = s5 "\t" 1 "\t" 2 - s6 = s6 "\t" 0 "\t" 0 - for (j = 1; j <= n; ++j) { - s1 = s1 "\t" p1 "x" p2 - s2 = s2 "\tC" (++numOffspring) - s3 = s3 "\t" p1 - s4 = s4 "\t" p2 - s5 = s5 "\t0" - s6 = s6 "\t0" - } - } - print s1 - print s2 - print s3 - print s4 - print s5 - print s6 - } - s = $1 "\t" chr "\t" $2 "\t" $3 - for (i = 7; i<=NF; i+=3) { - if (fullData) #parental data - s = s "\t1 2\t1 2" - n = length($i) / 2 - p1 = substr($i,1,n) - p2 = substr($i,n+1) - for (j = 1; j <= n; ++j) - s = s "\t" map[substr(p1, j, 1) substr(p2, j, 1)] - } - print s -} diff --git a/software/LepMap3/scripts/order2data.awk b/software/LepMap3/scripts/order2data.awk deleted file mode 100644 index 19e9e0f..0000000 --- a/software/LepMap3/scripts/order2data.awk +++ /dev/null @@ -1,65 +0,0 @@ -#script for marker binning... -BEGIN{ -#ACxAG=AA,AC,AG,CG - map["AA"] = "1 0 0 0 0 0 0"#00 - map["AC"] = "0 1 0 0 0 0 0"#01 - map["AG"] = "0 0 1 0 0 0 0"#10 - map["CG"] = "0 0 0 0 0 1 0"#11 - - if (chr == "") - chr = 1 -} -/^[^#]/{ - for (j = 7; j <= NF; ++j) - if ($j ~ /#$/) { - $j = substr($j, 1, length($j) - 1) - oldNF = j - break - } - if (oldNF == NF) - next - if (prev == "" && pedigree) { - s1 = "CHR\tPOS" - s2 = "CHR\tPOS" - s3 = "CHR\tPOS" - s4 = "CHR\tPOS" - s5 = "CHR\tPOS" - s6 = "CHR\tPOS" - f = 1 - nt = 0 - for (j = 7; j <= oldNF; j+=3) { - n = length($j) / 2 - s1 = s1 "\tF" f "\tF" f - s2 = s2 "\t" (nt + 1) "\t" (nt + 2) - s3 = s3 "\t0\t0" - s4 = s4 "\t0\t0" - s5 = s5 "\t1\t2" - s6 = s6 "\t0\t0" - for (i = 1; i <= n; ++i) { - s1 = s1 "\tF" f - s2 = s2 "\t" (nt + i + 2) - s3 = s3 "\t" (nt + 1) - s4 = s4 "\t" (nt + 2) - s5 = s5 "\t0" - s6 = s6 "\t0" - } - nt += n + 2 - ++f - } - print s1 "\n" s2 "\n" s3 "\n" s4 "\n" s5 "\n" s6 - } - - s = "" - nt = 0 - for (j = 7; j <= oldNF; j+=3) { - s = s "\t" map["AC"] "\t" map["AG"] - n = length($j) / 2 - for (i = oldNF + nt + 1; i <= oldNF + nt + 4 *n; i+=4) - s = s "\t" $i " " $(i+1) " " $(i+2) " 0 0 " $(i+3) " 0" - nt += 4 * n - } - if (prev != s || FILENAME != prevFN) - print $1 "\t" chr s - prev = s - prevFN = FILENAME -} diff --git a/software/LepMap3/scripts/phasematch.awk b/software/LepMap3/scripts/phasematch.awk deleted file mode 100644 index 100935a..0000000 --- a/software/LepMap3/scripts/phasematch.awk +++ /dev/null @@ -1,133 +0,0 @@ -#awk -f phasematch.awk order_reference.txt order_mapped.txt >order_mapped_in_reference_phase.txt -# -BEGIN{ -} -(NR==FNR && /^[^#]/){ - for (f = 7; f < NF; f+=3) - refdata[$1, f] = $f - if (numF != "" && numF != NF) { - print "Error: different number of columns in the input orders" > "/dev/stderr" - exit 1 - } - numF = NF -} - -(NR!=FNR){ - if (/^[#]/) - ; - else { - if (numF != NF) { - print "Error: different number of columns in the input orders" > "/dev/stderr" - exit 1 - } - } - data[FNR]=$0 -} - -END{ - for (i = 1; i <= FNR; ++i) { - $0 = data[i] - if (/^[#]/) - ; - else { - for (f = 7; f < NF; f+=3) { - if (($1 SUBSEP f) in refdata) { - ham1[f] += hamming1($f, refdata[$1, f]) - maxham1[f] += maxh - ham2[f] += hamming2($f, refdata[$1, f]) - maxham2[f] += maxh - } - } - } - - } -for (f = 7; f < NF; f+=3) { - print "***" > "/dev/stderr" - print "hamming distance1 is " ham1[f] " of " maxham1[f] " (" abs(ham1[f])/(maxham1[f]+0.000000000000000001) ") for family " ++family > "/dev/stderr" - print "hamming distance2 is " ham2[f] " of " maxham2[f] " (" abs(ham2[f])/(maxham2[f]+0.000000000000000001) ") for family " family > "/dev/stderr" - } - - for (i = 1; i <= FNR; ++i) { - $0 = data[i] - if (/^[#]/) - print - else { - for (f = 7; f < NF; f+=3) { - n = length($f) / 2 - p1 = substr($f, 1, n) - p2 = substr($f, n + 1) - if (ham1[f] < 0) - p1 = flip(p1) - if (ham2[f] < 0) - p2 = flip(p2) - $f = p1 p2 - } - - - s = $1 "\t" $2 "\t" $3 "\t" $4 " " $5 " " $6 - for (f = 7; f < NF; f+=3) - s = s "\t" $f " " $(f+1) " " $(f+2) - print s - } - - } -} -function abs(x) { - if (x < 0) - return -x - return x -} - -function flip(x) { - gsub(/0/, "x", x) - gsub(/1/, "0", x) - gsub(/x/, "1", x) - return x -} - -function hamming1(x, y ,i,xi,yi,ret, n) -{ - n = length(y) - if (length(x) < n) - n = length(x) -# print x " " y - ret = 0 - maxh = 0 - n = n / 2 - for (i = 1; i <= n; ++i) { - xi = substr(x, i, 1) - yi = substr(y, i, 1) - if (yi != "-" && xi != "-") { - ++maxh - if (xi == yi) { - ++ret - } - else - --ret - } - } - return ret -} -function hamming2(x, y ,i,xi,yi,ret, n) -{ - n = length(y) - if (length(x) < n) - n = length(x) -# print x " " y - ret = 0 - maxh = 0 - for (i = n / 2 + 1; i <= n; ++i) { - xi = substr(x, i, 1) - yi = substr(y, i, 1) - if (yi != "-" && xi != "-") { - ++maxh - if (xi == yi) { - ++ret - } - else - --ret - } - } - return ret -} - diff --git a/software/LepMap3/scripts/pileup2posterior.awk b/software/LepMap3/scripts/pileup2posterior.awk deleted file mode 100644 index 84c6873..0000000 --- a/software/LepMap3/scripts/pileup2posterior.awk +++ /dev/null @@ -1,156 +0,0 @@ -#samtools mpileup -q 10 -Q 10 -s `cat sorted_bams`|awk -f pileupParser2.awk|awk -f pileup2posterior.awk -#needs mapping.txt -#Part of Lep-MAP3 -BEGIN{ - if (limit7 == "") # maximum quality of a read base - limit7 = 0.001 - - map["a"]=1 - map["A"]=1 - - map["c"]=2 - map["C"]=2 - - map["g"]=3 - map["G"]=3 - - map["t"]=4 - map["T"]=4 - - "cat mapping.txt"|getline - if (NF == 0) { - print "Error: file mapping.txt not found!" > "/dev/stderr" - exit - } - s = "CHR\tPOS" - - numIndividuals = 0 - for (i = 1; i <= NF; ++i) { - mapping[i] = $i - if (!($i in imapping)) - listOrder[++numIndividuals] = $i - ++imapping[$i] - } - print "Number of bams = " NF > "/dev/stderr" - print "Number of individuals = " numIndividuals > "/dev/stderr" - close("cat mapping.txt") - - - for (mi = 1; mi <= numIndividuals; ++mi) { - s = s "\t" listOrder[mi] - } - print s - FS="\t" - - #ascii characters mapping to their quality - for (i = 33; i <= 127; ++i) { - if (i == 33) # q == 0 - q = 0.75 # all bases equal probable - else if (i == 34) # q == 1 - q = 0.5 * (0.75 + 10 ^ (-0.15)) # One base must be more probable than others, e.g. p \in ]10^-0.15, 0.75[ - else - q = 10 ^ (-0.1 * (i - 33)) - quality[sprintf("%c", i)] = q - } - - for (i = 33; i <= 127; ++i) - for (j = 33; j <= 127; ++j) { - qs1 = sprintf("%c", i) - qs2 = sprintf("%c", j) - p = combineQ(qs1, qs2) - if (p < limit7) - p = limit7 - - logP[qs1 qs2] = log(p / 3) - logNP[qs1 qs2] = log(1 - p) - logNP2[qs1 qs2] = log(p / 3 + (1 - p)) + log(0.5) - } - - - logHalf = log(0.5) - logTwo = log(2.0) -} - -#function phred2p(q , p) -#{ -# p = ord[q] - 33 -# if (p == 0) -# return 0.75; -# if (p == 1) -# return 0.725; -# else -# return 10^(-0.1 * p) -#} - - -function combineQ(q1, q2 ,ret,p1,p2) -{ - p1 = quality[q1] - p2 = quality[q2] - ret = p1 + p2 - p1 * p2 - if (ret > 0.75) - ret = 0.75 - return ret -} - -function myexp(x) -{ - if (x < -100) - return 0 - else - return exp(x) -} - -{ - delete prob - for (i = 5; i <= NF; i+=4) - for (j = 1; j <= length($i); ++j) { - a = substr($i, j, 1) - if (a in map) { - am = map[a] - individual = mapping[int(i / 4)] - - #p = combineQ(substr($(i+1), j, 1), substr($(i+2), j, 1)) - #if (p < limit7) - # p = limit7 - #logP = log(p / 3) - #logNP = log(1 - p) - #logNP2 = log(p / 3 + (1 - p)) + logHalf - #faster by using a table... - qs = substr($(i+1), j, 1) substr($(i+2), j, 1) - - for (k = 1; k <= 4; ++k) - if (k == am) - prob[individual, k, k] += logNP[qs] - else - prob[individual, k, k] += logP[qs] - - for (k = 1; k < 4; ++k) - for (l = k + 1; l <= 4; ++l) - if (k == am || l == am) - prob[individual, k, l] += logNP2[qs] - else - prob[individual, k, l] += logP[qs] - } - } - s = $1 "\t" $2 - for (mi = 1; mi <= numIndividuals; ++mi) { - m = listOrder[mi] - - maxp = prob[m,1,1] + 0 - for (k = 1; k <= 4; ++k) - for (l = k; l <= 4; ++l) - if (prob[m, k, l] + 0 > maxp) - maxp = prob[m, k, l] + 0 - - for (k = 1; k <= 4; ++k) - for (l = k; l <= 4; ++l) { - if (k == 1 && l == 1) - s = s "\t" - else - s = s " " - s = s myexp(prob[m, k, l] - maxp) - } - } - print s -} diff --git a/software/LepMap3/scripts/pileupParser2.awk b/software/LepMap3/scripts/pileupParser2.awk deleted file mode 100644 index 4f382c0..0000000 --- a/software/LepMap3/scripts/pileupParser2.awk +++ /dev/null @@ -1,90 +0,0 @@ -#samtools mpileup -q 10 -Q 10 -s `cat sorted_bams`|awk -f pileupParser2.awk|awk -f pileup2posterior.awk -#needs mapping.txt -#Part of Lep-MAP3 -BEGIN{ - "cat mapping.txt"|getline - if (NF == 0) { - print "Error: file mapping.txt not found!" > "/dev/stderr" - exit - } - numIndividuals = 0 - for (i = 1; i <= NF; ++i) { - mapping[i] = $i - if (!($i in ind)) - ++numIndividuals - ind[$i] - } - close("cat mapping.txt") - - - if (limit1 == "") - limit1 = 3 # coverage per individual - if (limit2 == "") - limit2 = 0.3 * numIndividuals # number of allowed individuals with lower coverage - if (limit3 == "") - limit3 = 0.1 * numIndividuals # minimum allele coverage - if (limit4 == "") - limit4 = limit1 * (numIndividuals - limit2) # minimum total counts - - print "PileupParser2 parameters: limit1=" limit1 " limit2=" limit2 " limit3=" limit3 " limit4=" limit4 > "/dev/stderr" - - FS="\t" - OFS="\t" -} - -function abs(a) -{ - if (a < 0) - return -a - else - return a -} - -{ - sum = 0 - for (i = 4; i <= NF; i+=4) - sum += $i; - - if (sum < limit4) - next - - delete count - for (i = 4; i <= NF; i+=4) - count[mapping[int(i / 4)]]+=$i - - missing = 0 - for (ci in count) - if (count[ci] < limit1) - ++missing - - if (missing > limit2) - next - - delete c - for (i = 5; i <= NF; i+=4) { - if ($(i-1) == 0) - $i = "" - gsub(/\$/,"",$i) #remove end of reads - gsub(/\^./,"",$i) #remove quality - while (match($i, /[+-][1-9][0-9]*/) > 0) { #remove indels - $i = substr($i, 1, RSTART - 1) substr($i, RSTART + RLENGTH + substr($i, RSTART + 1, RLENGTH - 1)) - } - tmp = $i - c[0] += gsub(/[Aa]/, "", tmp) - c[1] += gsub(/[Cc]/, "", tmp) - c[2] += gsub(/[Gg]/, "", tmp) - c[3] += gsub(/[Tt]/, "", tmp) - } - alleles = 0 - if (c[0] >= limit3) - ++alleles; - if (c[1] >= limit3) - ++alleles; - if (c[2] >= limit3) - ++alleles; - if (c[3] >= limit3) - ++alleles; - - if (alleles >= 2) - print -} diff --git a/software/LepMap3/scripts/somePaternal.awk b/software/LepMap3/scripts/somePaternal.awk deleted file mode 100644 index 51234b1..0000000 --- a/software/LepMap3/scripts/somePaternal.awk +++ /dev/null @@ -1,26 +0,0 @@ -#flips informative markers based on a map -#awk -f somePaternal map.txt data.call -BEGIN{ - FS="\t" - OFS="\t" -} - -(NR==FNR && /^[^#]/) { - map[++markers] = $1 -} - -(NR!=FNR && FNR<=7){print} - -(NR!=FNR && FNR==2){for (i = 3; i<=NF; ++i) {if (!($i in d)) d[$i] = ++p; f[i]=d[$i]}} -(NR!=FNR && FNR==4){for (i = 3; i<=NF; ++i) if ($i==0) pa[f[i], ++count[f[i]]]=i} - -(NR!=FNR && FNR>7){ - for (j = 1; j <= p; ++j) { - if (map[FNR-7]>0) { - tmp = $(pa[j, 1]) - $(pa[j, 1]) = $(pa[j, 2]) - $(pa[j, 2]) = tmp - } - } - print -} From f4f081ad3c2633ad9caccce2a3c3f222bcd1be06 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Wed, 2 Feb 2022 16:50:24 -0500 Subject: [PATCH 03/46] update place/orient propogation --- rules/LepAnchor/LepAnchor.smk | 6 +- .../{place_orient.smk => place_orient1.smk} | 23 +++-- rules/LepAnchor/place_orient2.smk | 87 +++++++++++++++++++ rules/LepAnchor/place_orient3.smk | 50 +++++++++++ rules/LepAnchor/place_orient_ii.smk | 60 ------------- rules/LepAnchor/place_orient_iii.smk | 74 ---------------- 6 files changed, 158 insertions(+), 142 deletions(-) rename rules/LepAnchor/{place_orient.smk => place_orient1.smk} (63%) create mode 100644 rules/LepAnchor/place_orient2.smk create mode 100644 rules/LepAnchor/place_orient3.smk delete mode 100644 rules/LepAnchor/place_orient_ii.smk delete mode 100644 rules/LepAnchor/place_orient_iii.smk diff --git a/rules/LepAnchor/LepAnchor.smk b/rules/LepAnchor/LepAnchor.smk index a746cef..62502be 100644 --- a/rules/LepAnchor/LepAnchor.smk +++ b/rules/LepAnchor/LepAnchor.smk @@ -21,9 +21,9 @@ lg_range = list(range(1,lg+1)) include: "generate_inputs.smk" include: "mask_and_chain.smk" -include: "place_orient.smk" -include: "place_orient_ii.smk" -include: "place_orient_iii.smk" +include: "place_orient1.smk" +include: "place_orient2.smk" +include: "place_orient3.smk" include: "build_agp.smk" include: "build_fasta.smk" include: "mareymaps_untrimmed.smk" diff --git a/rules/LepAnchor/place_orient.smk b/rules/LepAnchor/place_orient1.smk similarity index 63% rename from rules/LepAnchor/place_orient.smk rename to rules/LepAnchor/place_orient1.smk index dd3546c..ccf2ffe 100644 --- a/rules/LepAnchor/place_orient.smk +++ b/rules/LepAnchor/place_orient1.smk @@ -7,11 +7,11 @@ rule place_orient: lift = "10_PlaceAndOrientContigs/liftover.la" output: chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", - haplos = "10_PlaceAndOrientContigs/orient_1/haplotypes/chr.{lg_range}.haplo.suspected" + haplos = "10_PlaceAndOrientContigs/orient_1/haplotypes/chr.{lg_range}.haplo.suspected", + chromerr = report("10_PlaceAndOrientContigs/orient_1/logs/chr.{lg_range}.la.log", category = "Anchoring I Logs") log: - chrom = report("10_PlaceAndOrientContigs/orient_1/logs/chr.{lg_range}.la.log", category = "Anchoring I Logs"), - haplos = "10_PlaceAndOrientContigs/orient_1/haplotypes/chr.{lg_range}.haplo.all", - errors = "10_PlaceAndOrientContigs/orient_1/errors/chr.{lg_range}.errors" + errors = "10_PlaceAndOrientContigs/orient_1/errors/chr.{lg_range}.errors", + haplos = "10_PlaceAndOrientContigs/orient_1/haplotypes/chr.{lg_range}.haplo.all" params: chrom = "{lg_range}", extras = place_orient_extra, @@ -21,12 +21,25 @@ rule place_orient: message: "Running the 1st round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {log.chrom} + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} sort -n -r {log.chrom} | awk '($NF=="haplotype" && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {log.haplos} sort -n -r {log.chrom} | awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} grep "error$" {log.chrom} > {log.errors} """ +rule propogate1: + input: + placed = expand("10_PlaceAndOrientContigs/orient_1/chr.{lgs}.la", lgs = lg_range), + errors = expand("10_PlaceAndOrientContigs/orient_1/logs/chr.{lgs}.la.log", lgs = lg_range), + bedfile = "10_PlaceAndOrientContigs/map_extra.bed" + output: + propogated = "10_PlaceAndOrientContigs/map.propogated.bed", + message: "First round of propogation with propogate4.awk" + shell: + """ + awk -f software/LepAnchor/scripts/propagate4.awk pass=1 {input.placed} pass=2 {input.errors} | awk -f software/LepAnchor/scripts/pickbed.awk - {input.bedfile} > {output.propogated} + #awk -f propagate4.awk pass=1 chr*[0-9].la pass=2 chr*[0-9].la.err|awk -f pickbed.awk - map_extra.bed >map_extra_prop.bed + """ rule mergehaplos: input: expand("10_PlaceAndOrientContigs/orient_1/haplotypes/chr.{lg}.haplo.suspected", lg = lg_range) diff --git a/rules/LepAnchor/place_orient2.smk b/rules/LepAnchor/place_orient2.smk new file mode 100644 index 0000000..3dc38ec --- /dev/null +++ b/rules/LepAnchor/place_orient2.smk @@ -0,0 +1,87 @@ +#rule liftover2: +# input: +# chain = "9_Chain/chainfile.gz", +# intervals = "10_PlaceAndOrientContigs/lepanchor.input", +# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.before", +# haplos2 = "10_PlaceAndOrientContigs/suspected.haplotypes.after", +# lengths = "10_PlaceAndOrientContigs/contigs.length" +# output: +# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.all", +# lift = report("10_PlaceAndOrientContigs/liftover.nohaplotypes.la", category = "Lifted Intervals"), +# sortedlift = report("10_PlaceAndOrientContigs/liftover.sorted.nohaplotypes.la", category = "Lifted Intervals"), +# mapfile = "10_PlaceAndOrientContigs/map.nohaplotypes.clean", +# bedfile = "10_PlaceAndOrientContigs/map.nohaplotypes.bed", +# unused = "10_PlaceAndOrientContigs/not_used.nohaplotypes.txt", +# chr0 = "10_PlaceAndOrientContigs/chr0.nohaplotypes.bed", +# mapextra = "10_PlaceAndOrientContigs/map.nohaplotypes.extra.bed" +# message: "Recreating bedfile omitting haplotypes discovered from PlaceAndOrientContigs" +# params: +# chrom = lg +# shell: +# """ +# cat {input.haplos} {input.haplos2} > {output.haplos} +# gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.intervals} haplotypes={output.haplos} chain=- > {output.lift} +# cat {output.lift} | sort -V -k 1,1 -k 2,2n > {output.sortedlift} +# java -cp software/LepAnchor CleanMap map={output.sortedlift} > {output.mapfile} +# java -cp software/LepAnchor Map2Bed map={output.mapfile} contigLength={input.lengths} > {output.bedfile} +# cut -f 1 {input.lengths} | grep -v -w -F -f <(cut -f 2 {output.haplos}; cut -f 1 {output.bedfile}) > {output.unused} +# grep -w -F -f {output.unused} {input.lengths} | awk -vn={params.chrom} '{{s=$1"\t1\t"$2"\t?\t"; for (i=1;i<=n;++i) print s i}}' > {output.chr0} +# cat {output.bedfile} {output.chr0} > {output.mapextra} +# """ + + +rule place_orient2: + input: + chain = "9_Chain/chainfile.gz", + bedfile = "10_PlaceAndOrientContigs/map.propogated.bed", + paf = paf, + prox = proximity, + lift = "10_PlaceAndOrientContigs/liftover.la", + chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la" + output: + chrom = "10_PlaceAndOrientContigs/orient_2/chr.{lg_range}.la", + haplos = "10_PlaceAndOrientContigs/orient_2/haplotypes/chr.{lg_range}.haplo.suspected", + chromerr = report("10_PlaceAndOrientContigs/orient_2/logs/chr.{lg_range}.la.log", category = "Anchoring II Logs") + log: + haplos = "10_PlaceAndOrientContigs/orient_2/haplotypes/chr.{lg_range}.haplo.all", + errors = "10_PlaceAndOrientContigs/orient_2/errors/chr.{lg_range}.errors" + params: + chrom = "{lg_range}", + extras = place_orient_extra, + datatype = data_type, + haplo = haplo_limit + threads: 3 + message: "Running 2nd round of PlaceAndOrientContigs for linkage group {params.chrom}" + shell: + """ + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} + sort -n -r {output.chromrr} | awk '($NF=="haplotype" && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {log.haplos} + sort -n -r {output.chromerr} | awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} + grep "error$" {log.chromerr} > {log.errors} + """ + + rule propogate2: + input: + placed = expand("10_PlaceAndOrientContigs/orient_2/logs/chr.{lgs}.la.log", lgs = lg_range), + bedfile = "10_PlaceAndOrientContigs/map.propogated.bed" + output: + prop = expand("10_PlaceAndOrientContigs/propogate/propogated.{lgs}.la", lgs = range(lg + 1)), + propogated = "10_PlaceAndOrientContigs/map.propogated2.bed", + iter1 = temp("10_PlaceAndOrientContigs/tmp1.la"), + iter2 = temp("10_PlaceAndOrientContigs/tmp2.la") + message: "Second round of propogation" + shell: + """ + awk -f software/LepAnchor/scripts/propagate.awk {input.placed} > 10_PlaceAndOrientContigs/tmp1.la + awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp1.la > 10_PlaceAndOrientContigs/tmp2.la + i=2 + + while ! cmp -s "10_PlaceAndOrientContigs/tmp$i.la" "10_PlaceAndOrientContigs/tmp$(( $i-1 )).la" ;do + awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp$i.la > 10_PlaceAndOrientContigs/tmp$[$i+1].la + i=$[$i+1] + done + + #create prop*.la + awk -f software/LepAnchor/scripts/propagate2.awk tmp$i.la | awk '(/^[^#]/ && NF>=8){{++d[$1"\t"($7+0)"\t"($8+0)]; data[++line]=$0}}END{{for (i=1; i<=line; ++i) {{$0=data[i];if (d[$1"\t"($7+0)"\t"($8+0)] == 1) {{fn="10_PlaceAndOrientContigs/propogate/propogated."$5".la";print $0>fn}}}}}}' + awk '{{print $1"\t"($7+0)"\t"($8+0)"\t?\t"$5}}' {output.prop} | awk -f software/LepAnchor/scripts/pickbed.awk - {input.bedfile} > {output.propogated} + """ \ No newline at end of file diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk new file mode 100644 index 0000000..2b05cfd --- /dev/null +++ b/rules/LepAnchor/place_orient3.smk @@ -0,0 +1,50 @@ +rule place_orient3: + input: + chain = "9_Chain/chainfile.gz", + bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", + paf = paf, + prox = proximity, + lift = "10_PlaceAndOrientContigs/liftover.nohaplotypes.la", + chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", + propogated = "10_PlaceAndOrientContigs/propogate/propogated.{lg_range}.la" + output: + chrom = "10_PlaceAndOrientContigs/orient_3/chr.{lg_range}.la", + haplos = "10_PlaceAndOrientContigs/orient_3/haplotypes/chr.{lg_range}.haplo.suspected" + log: + chrom = report("10_PlaceAndOrientContigs/orient_3/logs/chr.{lg_range}.la.log", category = "Anchoring III Logs"), + haplos = "10_PlaceAndOrientContigs/orient_3/haplotypes/chr.{lg_range}.haplo.all", + errors = "10_PlaceAndOrientContigs/orient_3/errors/chr.{lg_range}.errors" + params: + chrom = "{lg_range}", + extras = place_orient_extra, + datatype = data_type, + haplo = haplo_limit + message: "Running 3rd round of PlaceAndOrientContigs for linkage group {params.chrom}" + threads: 3 + shell: + """ + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {log.chrom} + sort -n -r {log.chrom} | awk '($NF=="haplotype" && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {log.haplos} + sort -n -r {log.chrom} | awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} + grep "error$" {log.chrom} > {log.errors} + """ + + +rule prune: + input: + oriented = expand("10_PlaceAndOrientContigs/orient_3/chr.{lgs}.la", lgs = lg_range), + bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed" + output: + pruned = report("10_PlaceAndOrientContigs/orient_3/pruned.la", category = "Logs"), + cleaned = report("10_PlaceAndOrientContigs/overlaps_rm.la", category = "Logs") + message: "Pruning contig blocks without map support and removing overlaps" + params: + chrom = lg + shell: + """ + for i in $(seq {params.chrom}) + do + awk -f software/LepAnchor/scripts/prune.awk 10_PlaceAndOrientContigs/orient_3/ichr.$i.la > 10_PlaceAndOrientContigs/orient_3/ichr.${{i}}.pruned.la + done 2> {output.pruned} + awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} 10_PlaceAndOrientContigs/orient_3/ichr.*.pruned.la > {output.cleaned} + """ \ No newline at end of file diff --git a/rules/LepAnchor/place_orient_ii.smk b/rules/LepAnchor/place_orient_ii.smk deleted file mode 100644 index 4bbb5f6..0000000 --- a/rules/LepAnchor/place_orient_ii.smk +++ /dev/null @@ -1,60 +0,0 @@ -rule liftover2: - input: - chain = "9_Chain/chainfile.gz", - intervals = "10_PlaceAndOrientContigs/lepanchor.input", - haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.before", - haplos2 = "10_PlaceAndOrientContigs/suspected.haplotypes.after", - lengths = "10_PlaceAndOrientContigs/contigs.length" - output: - haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.all", - lift = report("10_PlaceAndOrientContigs/liftover.nohaplotypes.la", category = "Lifted Intervals"), - sortedlift = report("10_PlaceAndOrientContigs/liftover.sorted.nohaplotypes.la", category = "Lifted Intervals"), - mapfile = "10_PlaceAndOrientContigs/map.nohaplotypes.clean", - bedfile = "10_PlaceAndOrientContigs/map.nohaplotypes.bed", - unused = "10_PlaceAndOrientContigs/not_used.nohaplotypes.txt", - chr0 = "10_PlaceAndOrientContigs/chr0.nohaplotypes.bed", - mapextra = "10_PlaceAndOrientContigs/map.nohaplotypes.extra.bed" - message: "Recreating bedfile omitting haplotypes discovered from PlaceAndOrientContigs" - params: - chrom = lg - shell: - """ - cat {input.haplos} {input.haplos2} > {output.haplos} - gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.intervals} haplotypes={output.haplos} chain=- > {output.lift} - cat {output.lift} | sort -V -k 1,1 -k 2,2n > {output.sortedlift} - java -cp software/LepAnchor CleanMap map={output.sortedlift} > {output.mapfile} - java -cp software/LepAnchor Map2Bed map={output.mapfile} contigLength={input.lengths} > {output.bedfile} - cut -f 1 {input.lengths} | grep -v -w -F -f <(cut -f 2 {output.haplos}; cut -f 1 {output.bedfile}) > {output.unused} - grep -w -F -f {output.unused} {input.lengths} | awk -vn={params.chrom} '{{s=$1"\t1\t"$2"\t?\t"; for (i=1;i<=n;++i) print s i}}' > {output.chr0} - cat {output.bedfile} {output.chr0} > {output.mapextra} - """ - - -rule place_orient2: - input: - chain = "9_Chain/chainfile.gz", - bedfile = "10_PlaceAndOrientContigs/map.nohaplotypes.extra.bed", - paf = paf, - prox = proximity, - lift = "10_PlaceAndOrientContigs/liftover.nohaplotypes.la" - output: - chrom = "10_PlaceAndOrientContigs/orient_2/chr.{lg_range}.la", - haplos = "10_PlaceAndOrientContigs/orient_2/haplotypes/chr.{lg_range}.haplo.suspected" - log: - chrom = report("10_PlaceAndOrientContigs/orient_2/logs/chr.{lg_range}.la.log", category = "Anchoring II Logs"), - haplos = "10_PlaceAndOrientContigs/orient_2/haplotypes/chr.{lg_range}.haplo.all", - errors = "10_PlaceAndOrientContigs/orient_2/errors/chr.{lg_range}.errors" - params: - chrom = "{lg_range}", - extras = place_orient_extra, - datatype = data_type, - haplo = haplo_limit - threads: 3 - message: "Running 2nd round of PlaceAndOrientContigs for linkage group {params.chrom}" - shell: - """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {log.chrom} - sort -n -r {log.chrom} | awk '($NF=="haplotype" && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {log.haplos} - sort -n -r {log.chrom} | awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} - grep "error$" {log.chrom} > {log.errors} - """ \ No newline at end of file diff --git a/rules/LepAnchor/place_orient_iii.smk b/rules/LepAnchor/place_orient_iii.smk deleted file mode 100644 index 5938a5e..0000000 --- a/rules/LepAnchor/place_orient_iii.smk +++ /dev/null @@ -1,74 +0,0 @@ -rule propogate: - input: - placed = expand("10_PlaceAndOrientContigs/orient_2/chr.{lgs}.la", lgs = lg_range), - bedfile = "10_PlaceAndOrientContigs/map.nohaplotypes.bed" - output: - propogated = "10_PlaceAndOrientContigs/map.propogated.bed", - tmp_prop = temp(expand("10_PlaceAndOrientContigs/propogate/propogated.{lgs}.la", lgs = range(lg + 1))), - message: "Propogating ...something" - shell: - """ - awk -f software/LepAnchor/scripts/propagate.awk {input.placed} > 10_PlaceAndOrientContigs/tmp1.la - awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp1.la > 10_PlaceAndOrientContigs/tmp2.la - i=2 - - while ! cmp -s "10_PlaceAndOrientContigs/tmp$i.la" "10_PlaceAndOrientContigs/tmp$(( $i-1 )).la" ;do - awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp$i.la > 10_PlaceAndOrientContigs/tmp$[$i+1].la - i=$[$i+1] - done - #create prop*.la - awk '/^[^#]/{{++d[$1 "\t" $7+0 "\t" $8+0]; data[++line]=$0}}END{{for (i = 1; i <= line; ++i) {{$0=data[i];if (d[$1 "\t" $7+0 "\t" $8+0] == 1) fn="10_PlaceAndOrientContigs/propogate/propogated."$5".la"; else if ($5==1) fn="10_PlaceAndOrientContigs/propogate/propogated.0.la"; else fn=""; if (fn != "") print $0>fn}}}}' 10_PlaceAndOrientContigs/tmp$i.la - - #create a new bed by combining propogated.[1-9]*.la and map.nohaplotypes.bed - awk '(NR==FNR){{print;c[$1]}}(NR!=FNR && !($1 in c)){{print $1 "\t" $7+0 "\t" $8+0"\t?\t"$5}}' {input.bedfile} {output.tmp_prop} > {output.propogated} - rm 10_PlaceAndOrientContigs/tmp*.la - """ - - -rule place_orient3: - input: - chain = "9_Chain/chainfile.gz", - bedfile = "10_PlaceAndOrientContigs/map.propogated.bed", - paf = paf, - prox = proximity, - lift = "10_PlaceAndOrientContigs/liftover.nohaplotypes.la" - output: - chrom = "10_PlaceAndOrientContigs/orient_3/ichr.{lg_range}.la", - haplos = "10_PlaceAndOrientContigs/orient_3/haplotypes/chr.{lg_range}.haplo.suspected" - log: - chrom = report("10_PlaceAndOrientContigs/orient_3/logs/ichr.{lg_range}.la.log", category = "Anchoring III Logs"), - haplos = "10_PlaceAndOrientContigs/orient_3/haplotypes/chr.{lg_range}.haplo.all", - errors = "10_PlaceAndOrientContigs/orient_3/errors/chr.{lg_range}.errors" - params: - chrom = "{lg_range}", - extras = place_orient_extra, - datatype = data_type, - haplo = haplo_limit - message: "Running 3rd round of PlaceAndOrientContigs for linkage group {params.chrom}" - shell: - """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {log.chrom} - sort -n -r {log.chrom} | awk '($NF=="haplotype" && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {log.haplos} - sort -n -r {log.chrom} | awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} - grep "error$" {log.chrom} > {log.errors} - """ - - -rule prune: - input: - oriented = expand("10_PlaceAndOrientContigs/orient_3/ichr.{lgs}.la", lgs = lg_range), - bedfile = "10_PlaceAndOrientContigs/map.propogated.bed" - output: - pruned = report("10_PlaceAndOrientContigs/orient_3/pruned.la", category = "Logs"), - cleaned = report("10_PlaceAndOrientContigs/overlaps_rm.la", category = "Logs") - message: "Pruning contig blocks without map support and removing overlaps" - params: - chrom = lg - shell: - """ - for i in $(seq {params.chrom}) - do - awk -f software/LepAnchor/scripts/prune.awk 10_PlaceAndOrientContigs/orient_3/ichr.$i.la > 10_PlaceAndOrientContigs/orient_3/ichr.${{i}}.pruned.la - done 2> {output.pruned} - awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} 10_PlaceAndOrientContigs/orient_3/ichr.*.pruned.la > {output.cleaned} - """ \ No newline at end of file From 9b76a78b41288a9f290c5a1f9e6a38d318824603 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Wed, 2 Feb 2022 16:58:44 -0500 Subject: [PATCH 04/46] finish updaing placeorient --- rules/LepAnchor/build_agp.smk | 6 +++--- rules/LepAnchor/place_orient2.smk | 32 ------------------------------- rules/LepAnchor/place_orient3.smk | 2 +- 3 files changed, 4 insertions(+), 36 deletions(-) diff --git a/rules/LepAnchor/build_agp.smk b/rules/LepAnchor/build_agp.smk index a3f19bc..a277777 100644 --- a/rules/LepAnchor/build_agp.smk +++ b/rules/LepAnchor/build_agp.smk @@ -1,6 +1,6 @@ rule construct_agp: input: - cleaned = "10_PlaceAndOrientContigs/overlaps_rm.la" + cleaned = "10_PlaceAndOrientContigs/overlaps.removed.la" output: agp = report("11_AGP/contigs/chr.{lg_range}.agp", category = "Contig AGP Files"), scaff_agp = report("11_AGP/scaffolds/chr.{lg_range}.scaffolds.agp", category = "Scaffold AGP Files") @@ -18,14 +18,14 @@ rule unused: input: lengths = "10_PlaceAndOrientContigs/contigs.length", haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.before", - agp = expand("11_AGP/contigs/chr.{lgs}.agp", lgs = lg_range), + cleaned = "10_PlaceAndOrientContigs/overlaps.removed.la", output: txt = "11_AGP/not_used_final.txt", agp = "11_AGP/not_used.agp" message: "Finding unused contigs" shell: """ - cut -f 1 {input.lengths} | grep -v -w -F -f <(cut -f 2 {input.haplos};awk '($5!="U"){{print $6}}' {input.agp}) > {output.txt} + cut -f 1 {input.lengths} | grep -v -w -F -f <(cut -f2 {input.haplos}; cut -f1 {input.cleaned}) > {output.txt} grep -F -w -f {output.txt} {input.lengths} | awk '{{print $1,1,$2,1,"W",$1,1,$2,"+"}}' > {output.agp} """ diff --git a/rules/LepAnchor/place_orient2.smk b/rules/LepAnchor/place_orient2.smk index 3dc38ec..6137323 100644 --- a/rules/LepAnchor/place_orient2.smk +++ b/rules/LepAnchor/place_orient2.smk @@ -1,35 +1,3 @@ -#rule liftover2: -# input: -# chain = "9_Chain/chainfile.gz", -# intervals = "10_PlaceAndOrientContigs/lepanchor.input", -# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.before", -# haplos2 = "10_PlaceAndOrientContigs/suspected.haplotypes.after", -# lengths = "10_PlaceAndOrientContigs/contigs.length" -# output: -# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.all", -# lift = report("10_PlaceAndOrientContigs/liftover.nohaplotypes.la", category = "Lifted Intervals"), -# sortedlift = report("10_PlaceAndOrientContigs/liftover.sorted.nohaplotypes.la", category = "Lifted Intervals"), -# mapfile = "10_PlaceAndOrientContigs/map.nohaplotypes.clean", -# bedfile = "10_PlaceAndOrientContigs/map.nohaplotypes.bed", -# unused = "10_PlaceAndOrientContigs/not_used.nohaplotypes.txt", -# chr0 = "10_PlaceAndOrientContigs/chr0.nohaplotypes.bed", -# mapextra = "10_PlaceAndOrientContigs/map.nohaplotypes.extra.bed" -# message: "Recreating bedfile omitting haplotypes discovered from PlaceAndOrientContigs" -# params: -# chrom = lg -# shell: -# """ -# cat {input.haplos} {input.haplos2} > {output.haplos} -# gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.intervals} haplotypes={output.haplos} chain=- > {output.lift} -# cat {output.lift} | sort -V -k 1,1 -k 2,2n > {output.sortedlift} -# java -cp software/LepAnchor CleanMap map={output.sortedlift} > {output.mapfile} -# java -cp software/LepAnchor Map2Bed map={output.mapfile} contigLength={input.lengths} > {output.bedfile} -# cut -f 1 {input.lengths} | grep -v -w -F -f <(cut -f 2 {output.haplos}; cut -f 1 {output.bedfile}) > {output.unused} -# grep -w -F -f {output.unused} {input.lengths} | awk -vn={params.chrom} '{{s=$1"\t1\t"$2"\t?\t"; for (i=1;i<=n;++i) print s i}}' > {output.chr0} -# cat {output.bedfile} {output.chr0} > {output.mapextra} -# """ - - rule place_orient2: input: chain = "9_Chain/chainfile.gz", diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index 2b05cfd..a8776f8 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -36,7 +36,7 @@ rule prune: bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed" output: pruned = report("10_PlaceAndOrientContigs/orient_3/pruned.la", category = "Logs"), - cleaned = report("10_PlaceAndOrientContigs/overlaps_rm.la", category = "Logs") + cleaned = report("10_PlaceAndOrientContigs/overlaps.removed.la", category = "Logs") message: "Pruning contig blocks without map support and removing overlaps" params: chrom = lg From 5c2c92c417e135f1fdc5b38e5f1a84f8bd687110 Mon Sep 17 00:00:00 2001 From: "Pavel V. Dimens" Date: Wed, 2 Feb 2022 19:08:25 -0500 Subject: [PATCH 05/46] Update LepWrap --- LepWrap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LepWrap b/LepWrap index 4561ff7..8443514 100755 --- a/LepWrap +++ b/LepWrap @@ -1,4 +1,4 @@ -LepWrap#! /usr/bin/env bash +#! /usr/bin/env bash if which snakemake &>/dev/null; then foo=1 @@ -227,4 +227,4 @@ if [ $LA == "true" ]; then lepanchor $1 $CONF else echo "Skipping LepAnchor" -fi \ No newline at end of file +fi From caa87ffda4673b2175d0ed5d211f649d468e7d9e Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Thu, 3 Feb 2022 20:02:16 -0500 Subject: [PATCH 06/46] updates to fit LA updates --- rules/LepAnchor/LepAnchor.smk | 1 + rules/LepAnchor/build_agp.smk | 2 +- rules/LepAnchor/place_orient2.smk | 57 ++++++++++++-------------- rules/LepAnchor/place_orient3.smk | 68 ++++++++++++++++++------------- rules/LepAnchor/place_orient4.smk | 49 ++++++++++++++++++++++ 5 files changed, 115 insertions(+), 62 deletions(-) create mode 100644 rules/LepAnchor/place_orient4.smk diff --git a/rules/LepAnchor/LepAnchor.smk b/rules/LepAnchor/LepAnchor.smk index 62502be..e15c3fe 100644 --- a/rules/LepAnchor/LepAnchor.smk +++ b/rules/LepAnchor/LepAnchor.smk @@ -24,6 +24,7 @@ include: "mask_and_chain.smk" include: "place_orient1.smk" include: "place_orient2.smk" include: "place_orient3.smk" +include: "place_orient4.smk" include: "build_agp.smk" include: "build_fasta.smk" include: "mareymaps_untrimmed.smk" diff --git a/rules/LepAnchor/build_agp.smk b/rules/LepAnchor/build_agp.smk index a277777..125850f 100644 --- a/rules/LepAnchor/build_agp.smk +++ b/rules/LepAnchor/build_agp.smk @@ -17,7 +17,7 @@ rule construct_agp: rule unused: input: lengths = "10_PlaceAndOrientContigs/contigs.length", - haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.before", + haplos = "10_PlaceAndOrientContigs/fullHaplotypes50.txt", cleaned = "10_PlaceAndOrientContigs/overlaps.removed.la", output: txt = "11_AGP/not_used_final.txt", diff --git a/rules/LepAnchor/place_orient2.smk b/rules/LepAnchor/place_orient2.smk index 6137323..44cd219 100644 --- a/rules/LepAnchor/place_orient2.smk +++ b/rules/LepAnchor/place_orient2.smk @@ -8,48 +8,41 @@ rule place_orient2: chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la" output: chrom = "10_PlaceAndOrientContigs/orient_2/chr.{lg_range}.la", - haplos = "10_PlaceAndOrientContigs/orient_2/haplotypes/chr.{lg_range}.haplo.suspected", - chromerr = report("10_PlaceAndOrientContigs/orient_2/logs/chr.{lg_range}.la.log", category = "Anchoring II Logs") - log: - haplos = "10_PlaceAndOrientContigs/orient_2/haplotypes/chr.{lg_range}.haplo.all", - errors = "10_PlaceAndOrientContigs/orient_2/errors/chr.{lg_range}.errors" + chromerr = "10_PlaceAndOrientContigs/orient_2/logs/chr.{lg_range}.err" params: chrom = "{lg_range}", extras = place_orient_extra, datatype = data_type, haplo = haplo_limit - threads: 3 + threads: 5 message: "Running 2nd round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} - sort -n -r {output.chromrr} | awk '($NF=="haplotype" && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {log.haplos} - sort -n -r {output.chromerr} | awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} - grep "error$" {log.chromerr} > {log.errors} """ - rule propogate2: - input: - placed = expand("10_PlaceAndOrientContigs/orient_2/logs/chr.{lgs}.la.log", lgs = lg_range), - bedfile = "10_PlaceAndOrientContigs/map.propogated.bed" - output: - prop = expand("10_PlaceAndOrientContigs/propogate/propogated.{lgs}.la", lgs = range(lg + 1)), - propogated = "10_PlaceAndOrientContigs/map.propogated2.bed", - iter1 = temp("10_PlaceAndOrientContigs/tmp1.la"), - iter2 = temp("10_PlaceAndOrientContigs/tmp2.la") - message: "Second round of propogation" - shell: - """ - awk -f software/LepAnchor/scripts/propagate.awk {input.placed} > 10_PlaceAndOrientContigs/tmp1.la - awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp1.la > 10_PlaceAndOrientContigs/tmp2.la - i=2 +rule propogate2: + input: + placed = expand("10_PlaceAndOrientContigs/orient_2/logs/chr.{lgs}.err", lgs = lg_range), + bedfile = "10_PlaceAndOrientContigs/map.propogated.bed" + output: + prop = expand("10_PlaceAndOrientContigs/propogate/propogated.{lgs}.la", lgs = range(lg + 1)), + propogated = "10_PlaceAndOrientContigs/map.propogated2.bed", + iter1 = temp("10_PlaceAndOrientContigs/tmp1.la"), + iter2 = temp("10_PlaceAndOrientContigs/tmp2.la") + message: "Second round of propogation" + shell: + """ + awk -f software/LepAnchor/scripts/propagate.awk {input.placed} > 10_PlaceAndOrientContigs/tmp1.la + awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp1.la > 10_PlaceAndOrientContigs/tmp2.la + i=2 - while ! cmp -s "10_PlaceAndOrientContigs/tmp$i.la" "10_PlaceAndOrientContigs/tmp$(( $i-1 )).la" ;do - awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp$i.la > 10_PlaceAndOrientContigs/tmp$[$i+1].la - i=$[$i+1] - done + while ! cmp -s "10_PlaceAndOrientContigs/tmp$i.la" "10_PlaceAndOrientContigs/tmp$(( $i-1 )).la" ;do + awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp$i.la > 10_PlaceAndOrientContigs/tmp$[$i+1].la + i=$[$i+1] + done - #create prop*.la - awk -f software/LepAnchor/scripts/propagate2.awk tmp$i.la | awk '(/^[^#]/ && NF>=8){{++d[$1"\t"($7+0)"\t"($8+0)]; data[++line]=$0}}END{{for (i=1; i<=line; ++i) {{$0=data[i];if (d[$1"\t"($7+0)"\t"($8+0)] == 1) {{fn="10_PlaceAndOrientContigs/propogate/propogated."$5".la";print $0>fn}}}}}}' - awk '{{print $1"\t"($7+0)"\t"($8+0)"\t?\t"$5}}' {output.prop} | awk -f software/LepAnchor/scripts/pickbed.awk - {input.bedfile} > {output.propogated} - """ \ No newline at end of file + #create prop*.la + awk -f software/LepAnchor/scripts/propagate2.awk tmp$i.la | awk '(/^[^#]/ && NF>=8){{++d[$1"\t"($7+0)"\t"($8+0)]; data[++line]=$0}}END{{for (i=1; i<=line; ++i) {{$0=data[i];if (d[$1"\t"($7+0)"\t"($8+0)] == 1) {{fn="10_PlaceAndOrientContigs/propogate/propogated."$5".la";print $0>fn}}}}}}' + awk '{{print $1"\t"($7+0)"\t"($8+0)"\t?\t"$5}}' {output.prop} | awk -f software/LepAnchor/scripts/pickbed.awk - {input.bedfile} > {output.propogated} + """ \ No newline at end of file diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index a8776f8..26bf843 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -4,47 +4,57 @@ rule place_orient3: bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", paf = paf, prox = proximity, - lift = "10_PlaceAndOrientContigs/liftover.nohaplotypes.la", + lift = "10_PlaceAndOrientContigs/liftover.la", chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", propogated = "10_PlaceAndOrientContigs/propogate/propogated.{lg_range}.la" output: chrom = "10_PlaceAndOrientContigs/orient_3/chr.{lg_range}.la", - haplos = "10_PlaceAndOrientContigs/orient_3/haplotypes/chr.{lg_range}.haplo.suspected" - log: - chrom = report("10_PlaceAndOrientContigs/orient_3/logs/chr.{lg_range}.la.log", category = "Anchoring III Logs"), - haplos = "10_PlaceAndOrientContigs/orient_3/haplotypes/chr.{lg_range}.haplo.all", - errors = "10_PlaceAndOrientContigs/orient_3/errors/chr.{lg_range}.errors" + errors = "10_PlaceAndOrientContigs/orient_3/errors/chr.{lg_range}.err" params: chrom = "{lg_range}", extras = place_orient_extra, datatype = data_type, - haplo = haplo_limit message: "Running 3rd round of PlaceAndOrientContigs for linkage group {params.chrom}" - threads: 3 + threads: 5 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {log.chrom} - sort -n -r {log.chrom} | awk '($NF=="haplotype" && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {log.haplos} - sort -n -r {log.chrom} | awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} - grep "error$" {log.chrom} > {log.errors} + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} """ - - -rule prune: - input: - oriented = expand("10_PlaceAndOrientContigs/orient_3/chr.{lgs}.la", lgs = lg_range), - bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed" - output: - pruned = report("10_PlaceAndOrientContigs/orient_3/pruned.la", category = "Logs"), - cleaned = report("10_PlaceAndOrientContigs/overlaps.removed.la", category = "Logs") - message: "Pruning contig blocks without map support and removing overlaps" + +rule findhaplotypes: + input: + errors = expand("10_PlaceAndOrientContigs/orient_3/errors/chr.{lgs}.err", lgs = lg_range) + output: + haplos = "10_PlaceAndOrientContigs/fullHaplotypes50.txt" + message: "Finding full haplotypes" params: - chrom = lg + haplo = haplo_limit shell: """ - for i in $(seq {params.chrom}) - do - awk -f software/LepAnchor/scripts/prune.awk 10_PlaceAndOrientContigs/orient_3/ichr.$i.la > 10_PlaceAndOrientContigs/orient_3/ichr.${{i}}.pruned.la - done 2> {output.pruned} - awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} 10_PlaceAndOrientContigs/orient_3/ichr.*.pruned.la > {output.cleaned} - """ \ No newline at end of file + awk '($NF=="haplotype")' {input.errors} | + sort -n -r | + awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} + """ + +rule liftoverHaplotypes: + input: + chain = "9_Chain/chainfile.gz", + chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", + haplos = "10_PlaceAndOrientContigs/fullHaplotypes50.txt" + output: "10_PlaceAndOrientContigs/liftover/chr.{lg_range}.liftover" + message: "Running liftoverHaplotypes for {input.chrom}" + threads: 1 + shell: + """ + gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.chrom} haplotypes={input.haplos} chain=- > $i.liftover + """ + +rule removehaplotypes: + input: + mapfile = "10_PlaceAndOrientContigs/map.propogated2.bed", + haplos = "10_PlaceAndOrientContigs/fullHaplotypes50.txt" + output: + bedfile = "10_PlaceAndOrientContigs/map.propogated2.nohaplo.bed" + message: "Removing haplotypes from the map" + threads: 1 + shell: "awk -f software/LepAnchor/scripts/removeHaplotypes.awk {input} > {output}" diff --git a/rules/LepAnchor/place_orient4.smk b/rules/LepAnchor/place_orient4.smk new file mode 100644 index 0000000..6c46ec1 --- /dev/null +++ b/rules/LepAnchor/place_orient4.smk @@ -0,0 +1,49 @@ +rule place_orient4: + input: + chain = "9_Chain/chainfile.gz", + bedfile = "10_PlaceAndOrientContigs/map.propogated2.nohaplo.bed", + paf = paf, + prox = proximity, + lift = "10_PlaceAndOrientContigs/liftover.la", + chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", + chromlast = "10_PlaceAndOrientContigs/orient_3/chr.{lg_range}.la", + liftover = expand("10_PlaceAndOrientContigs/liftover/chr.{lgs}.liftover", lgs = lg_range) + output: + chrom = "10_PlaceAndOrientContigs/orient_4/chr.{lg_range}.la", + err = "10_PlaceAndOrientContigs/orient_4/errors/chr.{lg_range}.errors" + message: "Running 4th round of PlaceAndOrientContigs for linkage group {params.chrom}" + params: + chrom = "{lg_range}", + extras = place_orient_extra, + datatype = data_type, + threads: 5 + shell: + """ + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map=$MAPL chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} keepEmptyIntervals=1 evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} + """ + +rule prune_contigblocks: + input: "10_PlaceAndOrientContigs/orient_4/chr.{lg_range}.la" + output: + chrom = "10_PlaceAndOrientContigs/pruned/chr.{lg_range}.pruned.la", + err = temp("10_PlaceAndOrientContigs/pruned/err/chr.{lg_range}.pruned.err") + message: "Pruning contig blocks without map support and removing overlaps" + params: + chrom = lg + shell: "awk -f software/LepAnchor/scripts/prune.awk {input} > {output.chrom} 2> {output.err}" + +rule prune_post: + input: + bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", + prunedchrom = expand("10_PlaceAndOrientContigs/pruned/chr.{lgs}.pruned.la", lgs = lg_range), + prunederr = expand("10_PlaceAndOrientContigs/pruned/err/chr.{lgs}.pruned.err", lgs = lg_range) + output: + overlaps = "10_PlaceAndOrientContigs/overlaps.removed.la", + pruned = "10_PlaceAndOrientContigs/pruned.la" + message: "Removing overlaps" + threads: 1 + shell: + """ + cat {input.prunederr} > {output.pruned} + awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} + """ \ No newline at end of file From 0649e73daafa654a4d01b2f61f2c4c68ae61f121 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Thu, 3 Feb 2022 20:12:57 -0500 Subject: [PATCH 07/46] fix shebang --- LepWrap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LepWrap b/LepWrap index 4561ff7..8443514 100755 --- a/LepWrap +++ b/LepWrap @@ -1,4 +1,4 @@ -LepWrap#! /usr/bin/env bash +#! /usr/bin/env bash if which snakemake &>/dev/null; then foo=1 @@ -227,4 +227,4 @@ if [ $LA == "true" ]; then lepanchor $1 $CONF else echo "Skipping LepAnchor" -fi \ No newline at end of file +fi From 70e91a34b384b980e94d805ce59f87749532c740 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Thu, 3 Feb 2022 20:28:03 -0500 Subject: [PATCH 08/46] update i/o names --- rules/LepAnchor/build_agp.smk | 2 +- rules/LepAnchor/generate_inputs.smk | 6 +++--- rules/LepAnchor/mask_and_chain.smk | 17 ++++++++--------- rules/LepAnchor/place_orient3.smk | 8 ++++---- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/rules/LepAnchor/build_agp.smk b/rules/LepAnchor/build_agp.smk index 125850f..eb51e42 100644 --- a/rules/LepAnchor/build_agp.smk +++ b/rules/LepAnchor/build_agp.smk @@ -17,7 +17,7 @@ rule construct_agp: rule unused: input: lengths = "10_PlaceAndOrientContigs/contigs.length", - haplos = "10_PlaceAndOrientContigs/fullHaplotypes50.txt", + haplos = "10_PlaceAndOrientContigs/suspected.haplotypes", cleaned = "10_PlaceAndOrientContigs/overlaps.removed.la", output: txt = "11_AGP/not_used_final.txt", diff --git a/rules/LepAnchor/generate_inputs.smk b/rules/LepAnchor/generate_inputs.smk index 6c34c83..affe53d 100644 --- a/rules/LepAnchor/generate_inputs.smk +++ b/rules/LepAnchor/generate_inputs.smk @@ -36,7 +36,7 @@ rule contiglengths: rule find_haplotypes: input: "9_Chain/chainfile.gz" - output: report("10_PlaceAndOrientContigs/suspected.haplotypes.before", category = "Logs") + output: report("10_PlaceAndOrientContigs/suspected.haplotypes.initial", category = "Logs") message: "Finding non-haplotype contigs not included in map.bed" shell: """ @@ -48,7 +48,7 @@ rule liftover: input: chain = "9_Chain/chainfile.gz", intervals = "10_PlaceAndOrientContigs/lepanchor.input", - haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.before" + haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.initial" output: lift = report("10_PlaceAndOrientContigs/liftover.la", category = "Lifted Intervals"), sortedlift = report("10_PlaceAndOrientContigs/liftover.sorted.la", category = "Lifted Intervals") @@ -84,7 +84,7 @@ rule map2bed: rule ungrouped: input: lengths = "10_PlaceAndOrientContigs/contigs.length", - haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.before", + haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.initial", bedfile = "10_PlaceAndOrientContigs/map.bed" output: bedfile = "10_PlaceAndOrientContigs/map_extra.bed" diff --git a/rules/LepAnchor/mask_and_chain.smk b/rules/LepAnchor/mask_and_chain.smk index 2e26d64..d286810 100644 --- a/rules/LepAnchor/mask_and_chain.smk +++ b/rules/LepAnchor/mask_and_chain.smk @@ -1,10 +1,10 @@ rule prep_geno: input: geno - output: "8_Repeatmask/inputgenome/lepanchorinput.fa" + output: "8_RepeatMask/inputgenome/lepanchorinput.fa" message: "Preparing genome for repeat masking" shell: """ - mkdir -p 8_Repeatmask/inputgenome + mkdir -p 8_RepeatMask/inputgenome if (file {input} | grep -q compressed); then echo "- Assembly is compressed, creating decompressed copy: {output}" gunzip --stdout {input} > {output} @@ -16,23 +16,22 @@ rule prep_geno: rule repeatmask: - input: "8_Repeatmask/inputgenome/lepanchorinput.fa" - output: "8_Repeatmask/repeatmasked.fa.gz" - log: "8_Repeatmask/Red.log" + input: "8_RepeatMask/inputgenome/lepanchorinput.fa" + output: "8_RepeatMask/repeatmasked.fa.gz" + log: "8_RepeatMask/Red.log" message: "Using Red to repeat-mask {input}" threads: 30 shell: """ - echo "- Running Red" - software/LepAnchor/deps/Red -gnm 8_Repeatmask/inputgenome -msk 8_Repeatmask -sco 8_Repeatmask -cnd 8_Repeatmask -rpt 8_Repeatmask > {log} 2>> {log} + software/LepAnchor/deps/Red -gnm 8_RepeatMask/inputgenome -msk 8_RepeatMask -sco 8_RepeatMask -cnd 8_RepeatMask -rpt 8_RepeatMask > {log} 2>> {log} echo "- Compressing repeat-masked genome from Red" - gzip --stdout 8_Repeatmask/*.msk > {output} && rm 8_Repeatmask/*.msk + gzip --stdout 8_RepeatMask/*.msk > {output} && rm 8_RepeatMask/*.msk """ rule chain_1: input: - geno = "8_Repeatmask/repeatmasked.fa.gz", + geno = "8_RepeatMask/repeatmasked.fa.gz", ctrl = "software/LepAnchor/deps/all_lastz.ctl", scoremtx = "software/LepAnchor/deps/scoreMatrix.q" output: diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index 26bf843..0457664 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -21,11 +21,11 @@ rule place_orient3: gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} """ -rule findhaplotypes: +rule find_haplotypes2: input: errors = expand("10_PlaceAndOrientContigs/orient_3/errors/chr.{lgs}.err", lgs = lg_range) output: - haplos = "10_PlaceAndOrientContigs/fullHaplotypes50.txt" + haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" message: "Finding full haplotypes" params: haplo = haplo_limit @@ -40,7 +40,7 @@ rule liftoverHaplotypes: input: chain = "9_Chain/chainfile.gz", chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", - haplos = "10_PlaceAndOrientContigs/fullHaplotypes50.txt" + haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" output: "10_PlaceAndOrientContigs/liftover/chr.{lg_range}.liftover" message: "Running liftoverHaplotypes for {input.chrom}" threads: 1 @@ -52,7 +52,7 @@ rule liftoverHaplotypes: rule removehaplotypes: input: mapfile = "10_PlaceAndOrientContigs/map.propogated2.bed", - haplos = "10_PlaceAndOrientContigs/fullHaplotypes50.txt" + haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" output: bedfile = "10_PlaceAndOrientContigs/map.propogated2.nohaplo.bed" message: "Removing haplotypes from the map" From 4868a3aa8a87237ad12922bf8a5a7d9aa248660c Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Thu, 3 Feb 2022 20:31:17 -0500 Subject: [PATCH 09/46] rm redundant call --- rules/LepAnchor/place_orient4.smk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/LepAnchor/place_orient4.smk b/rules/LepAnchor/place_orient4.smk index 6c46ec1..b6fe2ab 100644 --- a/rules/LepAnchor/place_orient4.smk +++ b/rules/LepAnchor/place_orient4.smk @@ -19,7 +19,7 @@ rule place_orient4: threads: 5 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map=$MAPL chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} keepEmptyIntervals=1 evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map=$MAPL chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} """ rule prune_contigblocks: From e188863a4bfa68d08f533874f90a72eb1c5706bd Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Thu, 3 Feb 2022 22:31:30 -0500 Subject: [PATCH 10/46] cleanup --- rules/LepAnchor/place_orient1.smk | 19 +- rules/LepAnchor/place_orient2.smk | 5 +- rules/LepAnchor/place_orient3.smk | 2 +- rules/LepAnchor/place_orient4.smk | 2 +- software/LepAnchor/lepanchor_wrapper.sh | 246 ------------------------ 5 files changed, 6 insertions(+), 268 deletions(-) delete mode 100644 software/LepAnchor/lepanchor_wrapper.sh diff --git a/rules/LepAnchor/place_orient1.smk b/rules/LepAnchor/place_orient1.smk index ccf2ffe..8c4e3f3 100644 --- a/rules/LepAnchor/place_orient1.smk +++ b/rules/LepAnchor/place_orient1.smk @@ -7,24 +7,16 @@ rule place_orient: lift = "10_PlaceAndOrientContigs/liftover.la" output: chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", - haplos = "10_PlaceAndOrientContigs/orient_1/haplotypes/chr.{lg_range}.haplo.suspected", chromerr = report("10_PlaceAndOrientContigs/orient_1/logs/chr.{lg_range}.la.log", category = "Anchoring I Logs") - log: - errors = "10_PlaceAndOrientContigs/orient_1/errors/chr.{lg_range}.errors", - haplos = "10_PlaceAndOrientContigs/orient_1/haplotypes/chr.{lg_range}.haplo.all" params: chrom = "{lg_range}", extras = place_orient_extra, - datatype = data_type, - haplo = haplo_limit - threads: 3 + datatype = data_type + threads: 2 message: "Running the 1st round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} - sort -n -r {log.chrom} | awk '($NF=="haplotype" && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {log.haplos} - sort -n -r {log.chrom} | awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} - grep "error$" {log.chrom} > {log.errors} """ rule propogate1: @@ -38,11 +30,4 @@ rule propogate1: shell: """ awk -f software/LepAnchor/scripts/propagate4.awk pass=1 {input.placed} pass=2 {input.errors} | awk -f software/LepAnchor/scripts/pickbed.awk - {input.bedfile} > {output.propogated} - #awk -f propagate4.awk pass=1 chr*[0-9].la pass=2 chr*[0-9].la.err|awk -f pickbed.awk - map_extra.bed >map_extra_prop.bed """ - -rule mergehaplos: - input: expand("10_PlaceAndOrientContigs/orient_1/haplotypes/chr.{lg}.haplo.suspected", lg = lg_range) - output: "10_PlaceAndOrientContigs/suspected.haplotypes.after" - message: "Merging suspected haplotype contig information from the linkage groups" - shell: "cat {input} | sort | uniq > {output}" diff --git a/rules/LepAnchor/place_orient2.smk b/rules/LepAnchor/place_orient2.smk index 44cd219..6fffa40 100644 --- a/rules/LepAnchor/place_orient2.smk +++ b/rules/LepAnchor/place_orient2.smk @@ -12,9 +12,8 @@ rule place_orient2: params: chrom = "{lg_range}", extras = place_orient_extra, - datatype = data_type, - haplo = haplo_limit - threads: 5 + datatype = data_type + threads: 2 message: "Running 2nd round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index 0457664..bd1a60f 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -15,7 +15,7 @@ rule place_orient3: extras = place_orient_extra, datatype = data_type, message: "Running 3rd round of PlaceAndOrientContigs for linkage group {params.chrom}" - threads: 5 + threads: 2 shell: """ gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} diff --git a/rules/LepAnchor/place_orient4.smk b/rules/LepAnchor/place_orient4.smk index b6fe2ab..e6f9b74 100644 --- a/rules/LepAnchor/place_orient4.smk +++ b/rules/LepAnchor/place_orient4.smk @@ -16,7 +16,7 @@ rule place_orient4: chrom = "{lg_range}", extras = place_orient_extra, datatype = data_type, - threads: 5 + threads: 2 shell: """ gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map=$MAPL chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} diff --git a/software/LepAnchor/lepanchor_wrapper.sh b/software/LepAnchor/lepanchor_wrapper.sh deleted file mode 100644 index d80a0ab..0000000 --- a/software/LepAnchor/lepanchor_wrapper.sh +++ /dev/null @@ -1,246 +0,0 @@ -#!/bin/bash -########################################################## -# -# Lep-Anchor wrapper -# -# usage: lepanchor_wrapper.sh -f ref.fasta -n num_chr -c chain_file -p paf_file -r prox_file -m map_file1 -m map_file2, ... -# -# output: LA_REF.fa.gz anchored reference genome -# LA_REF.agp agp file describing the genome -# marey*.png Marey maps for visual verification -# chr*.agp agp files for each chromosome -# scaffolds_chr*.agp agp files for each chromosome in scaffolds (each block of linked contigs as a scaffold) -# -# -# Pasi Rastas, (c) 2020, pasi.rastas@gmail.com -# -########################################################## - -#parse parameters - -function print_usage() -{ -echo "##########################################################" -echo "#" -echo "# Lep-Anchor wrapper" -echo "#" -echo "# usage: lepanchor_wrapper.sh -t threads -f ref.fasta -n num_chr -c chain_file -r prox_file -p paf_file -m map_file1 -m map_file2, ... " -echo "#" -echo "# download Lep-Anchor by" -echo "# wget https://sourceforge.net/projects/lep-anchor/files/binary%2Bcode.zip/download -O la.zip;unzip la.zip" -echo "#" -echo "# Pasi Rastas, (c) 2020, pasi.rastas@gmail.com" -echo "#" -echo "##########################################################" -} - -while getopts ":n:c:p:m:f:t:r:" OPTION; do - case ${OPTION} in - t) - THREADS=$OPTARG;; - n) - CHR=$OPTARG;; - c) - CHAIN=$OPTARG;; - p) - PAF=$OPTARG;; - r) - PROX=$OPTARG;; - m) - MAP="$MAP $OPTARG";; - f) - REF=$OPTARG;; - *) - echo "Incorrect options provided" - exit 1;; - esac -done - -if [[ $MAP =~ ^$ ]];then - print_usage - echo "Please provide at least one map" - exit 1 -fi - -if [[ $REF =~ ^$ ]];then - if [[ ! -e "contigs.length" ]];then - print_usage - echo "Please provide either reference fasta or contigs.length file" - exit 1 - fi -fi - -if [[ $CHR =~ ^[0-9]+$ ]];then - echo "Number of chromosomes = $CHR" -else - print_usage - echo "Please provide parameter n (number of chromosomes)" - exit 1 -fi - -if [[ $CHAIN =~ ^$ ]];then - echo "No chain provided" - CHAIN="/dev/null" -fi - -if [[ $PROX =~ ^$ ]];then - echo "No proximity data provided" - PROX="/dev/null" -fi - - -if [[ $PAF =~ ^$ ]];then - echo "No paf provided" - PAF="/dev/null" -fi - -#number of threads -if [[ $THREADS =~ ^$ ]];then - THREADS=8 -fi - -#get Lep-Anchor -#wget https://sourceforge.net/projects/lep-anchor/files/binary%2Bcode.zip/download -O la.zip -#unzip la.zip - -#where Lep-Anchor binaries are located -LABIN=bin/ - -#java runtime located here -JAVA=java - -#parallel -if ! command -v "parallel" -then - echo "command parallel not found, using only one thread" - PARALLEL=bash -else - PARALLEL="parallel --jobs $THREADS" -fi - -if [[ ! $REF =~ ^$ ]];then - echo "calculating contigs.length file..." - echo "gunzip -fc $REF | awk -f scripts/contigLength.awk > contigs.length" | bash -fi - - -echo "finding full haplotypes..." -echo "gunzip -fc $CHAIN | awk -f scripts/findFullHaplotypes.awk > fullHaplotypes50.txt" | bash -wc -l fullHaplotypes50.txt - - -echo "running liftoverHaplotypes for all input maps..." -for i in $MAP -do -echo "gunzip -fc $CHAIN | $JAVA -cp $LABIN LiftoverHaplotypes map=$i haplotypes=fullHaplotypes50.txt chain=- > $i.liftover" -done | $PARALLEL - -#store lift overed maps to $MAPL -for i in $MAP -do - MAPL="$MAPL $i.liftover" -done - - -#make input for CleanMap -for i in $MAP -do - cat $i.liftover -done | sort -V -k 1,1 -k 2,2n > map_all_sorted.liftover - - -#CleanMap -echo "running CleanMap..." -$JAVA -cp $LABIN CleanMap map=map_all_sorted.liftover > map_all.clean - -#Map2Bed -echo "running Map2Bed..." -$JAVA -cp $LABIN Map2Bed map=map_all.clean contigLength=contigs.length > map.bed - -#find contigs not put into chromosomes -cut -f 1 contigs.length | grep -v -w -F -f <(cut -f 2 fullHaplotypes50.txt; cut -f 1 map.bed) > not_used.txt - -grep -w -F -f not_used.txt contigs.length | awk -vn=$CHR '{s=$1"\t1\t"$2"\t?\t"; for (i=1;i<=n;++i) print s i}' > chr0.bed -cat map.bed chr0.bed > map_extra.bed - - -#PlaceAndOrientContigs -echo "running PlaceAndOrientContigs (first iteration)..." -for i in $(seq $CHR) -do -echo "gunzip -fc $CHAIN | $JAVA -cp $LABIN PlaceAndOrientContigs bed=map_extra.bed chromosome=$i map=$MAPL chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=1 > chr$i.la 2> chr$i.la.err" -done | $PARALLEL - - -#propagate -echo "running propagate..." - -awk -f scripts/propagate.awk chr*.la > tmp1.la -awk -f scripts/propagate.awk tmp1.la > tmp2.la -i=2 - -while ! cmp -s "tmp$i.la" "tmp$(( $i-1 )).la" ;do - awk -f scripts/propagate.awk tmp$i.la > tmp$[$i+1].la - i=$[$i+1] -done - -#create prop*.la -awk '/^[^#]/{++d[$1 "\t" $7+0 "\t" $8+0]; data[++line]=$0}END{for (i = 1; i <= line; ++i) {$0=data[i];if (d[$1 "\t" $7+0 "\t" $8+0] == 1) fn="prop"$5".la"; else if ($5==1) fn="prop0.la"; else fn=""; if (fn != "") print $0>fn}}' tmp$i.la - -#create a new bed by combining prop[1-9]*.la and map.bed -awk '(NR==FNR){print;c[$1]}(NR!=FNR && !($1 in c)){print $1 "\t" $7+0 "\t" $8+0"\t?\t"$5}' map.bed prop[1-9]*.la > map_prop.bed - -#PlaceAndOrientContigs -echo "running PlaceAndOrientContigs (second iteration)..." -for i in $(seq $CHR) -do -echo "gunzip -fc $CHAIN | $JAVA -cp $LABIN PlaceAndOrientContigs bed=map_prop.bed chromosome=$i map=$MAPL chain=- paf=$PAF proximity=$PROX keepEmptyIntervals=1 > ichr$i.la 2> ichr$i.la.err" -done | $PARALLEL - -#pruning contig blocks without map support -for i in $(seq $CHR) -do - awk -f scripts/prune.awk ichr$i.la > ichr${i}_pruned.la -done 2> pruned.la - -#remove overlap(s) -awk -f scripts/removeOverlaps.awk map_prop.bed ichr*_pruned.la > iall.la - -#construct agp files -for i in $(seq $CHR) -do -awk -vn=$i '($5==n)' iall.la | awk -vprefix="LG" -vlg=$i -f scripts/makeagp_full2.awk - > chr$i.agp -awk -vn=$i '($5==n)' iall.la | awk -vprefix="LG" -vlg=$i -f scripts/makeagp2.awk - > scaffolds_chr$i.agp -done - -#find contigs not used -cut -f 1 contigs.length | grep -v -w -F -f <(cut -f 2 fullHaplotypes50.txt;awk '($5!="U"){print $6}' chr*.agp) > not_used_final.txt - -grep -F -w -f not_used_final.txt contigs.length | awk '{print $1,1,$2,1,"W",$1,1,$2,"+"}' > not_used.agp - -cat chr*.agp not_used.agp > REF_LA.agp -#one could use scaffolds_chr*.agp as well instead of chr*.agp -cat scaffolds_chr*.agp not_used.agp > REF_LA_scaffolds.agp - -#make final fasta -if [[ ! $REF =~ ^$ ]];then - echo "constructing final fasta (REF_LA.fa.gz)..." - echo "gunzip -fc $REF | awk -f scripts/makefasta.awk - REF_LA.agp | gzip > REF_LA.fa.gz" | bash - - echo "constructing final fasta (REF_LA_scaffolds.fa.gz)..." - echo "gunzip -fc $REF | awk -f scripts/makefasta.awk - REF_LA_scaffolds.agp | gzip > REF_LA_scaffolds.fa.gz" | bash -fi - -#construct Marey map -echo "constructing Marey maps... (marey*.png)" -j=1 -for m in $MAP -do - for c in $(seq $CHR) - do - awk -vn=$c '($3==n)' $m.liftover | awk -f scripts/liftover.awk chr$c.agp - |awk -vm=$j '(/LG/ && NF>=4){if (NF==4) $5=$4;print $1"\t"$2"\t"$3"\t"m"\t"$4"\t"$5}' - done - j=$[$j + 1] -done | gzip > marey.data.gz - -Rscript scripts/plot_marey.R \ No newline at end of file From 1d70ec57bad663e39fbd93cc33655290cb02dce2 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Thu, 3 Feb 2022 22:33:50 -0500 Subject: [PATCH 11/46] move chromosome={params.chrom} to front of call --- rules/LepAnchor/place_orient1.smk | 2 +- rules/LepAnchor/place_orient2.smk | 2 +- rules/LepAnchor/place_orient3.smk | 2 +- rules/LepAnchor/place_orient4.smk | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rules/LepAnchor/place_orient1.smk b/rules/LepAnchor/place_orient1.smk index 8c4e3f3..f25d197 100644 --- a/rules/LepAnchor/place_orient1.smk +++ b/rules/LepAnchor/place_orient1.smk @@ -16,7 +16,7 @@ rule place_orient: message: "Running the 1st round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} """ rule propogate1: diff --git a/rules/LepAnchor/place_orient2.smk b/rules/LepAnchor/place_orient2.smk index 6fffa40..d5a4611 100644 --- a/rules/LepAnchor/place_orient2.smk +++ b/rules/LepAnchor/place_orient2.smk @@ -17,7 +17,7 @@ rule place_orient2: message: "Running 2nd round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} """ rule propogate2: diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index bd1a60f..4df922d 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -18,7 +18,7 @@ rule place_orient3: threads: 2 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} """ rule find_haplotypes2: diff --git a/rules/LepAnchor/place_orient4.smk b/rules/LepAnchor/place_orient4.smk index e6f9b74..03b9503 100644 --- a/rules/LepAnchor/place_orient4.smk +++ b/rules/LepAnchor/place_orient4.smk @@ -19,7 +19,7 @@ rule place_orient4: threads: 2 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} chromosome={params.chrom} map=$MAPL chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map=$MAPL chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} """ rule prune_contigblocks: From e2073d7f9aee681ef8b5662d06ae15f628e097ff Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 12:50:02 -0500 Subject: [PATCH 12/46] fixes --- rules/LepAnchor/place_orient2.smk | 14 ++++++++------ rules/LepAnchor/place_orient3.smk | 4 ++-- rules/LepAnchor/place_orient4.smk | 7 +++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/rules/LepAnchor/place_orient2.smk b/rules/LepAnchor/place_orient2.smk index d5a4611..000934f 100644 --- a/rules/LepAnchor/place_orient2.smk +++ b/rules/LepAnchor/place_orient2.smk @@ -22,18 +22,20 @@ rule place_orient2: rule propogate2: input: - placed = expand("10_PlaceAndOrientContigs/orient_2/logs/chr.{lgs}.err", lgs = lg_range), + placed = expand("10_PlaceAndOrientContigs/orient_2/chr.{lgs}.la", lgs = lg_range), + #placed = expand("10_PlaceAndOrientContigs/orient_2/logs/chr.{lgs}.err", lgs = lg_range), bedfile = "10_PlaceAndOrientContigs/map.propogated.bed" output: - prop = expand("10_PlaceAndOrientContigs/propogate/propogated.{lgs}.la", lgs = range(lg + 1)), + prop = expand("10_PlaceAndOrientContigs/propogate/propogated.{lgs}.la", lgs = lg_range), propogated = "10_PlaceAndOrientContigs/map.propogated2.bed", iter1 = temp("10_PlaceAndOrientContigs/tmp1.la"), - iter2 = temp("10_PlaceAndOrientContigs/tmp2.la") + iter2 = temp("10_PlaceAndOrientContigs/tmp2.la"), + iter3 = temp("10_PlaceAndOrientContigs/tmp3.la") message: "Second round of propogation" shell: """ - awk -f software/LepAnchor/scripts/propagate.awk {input.placed} > 10_PlaceAndOrientContigs/tmp1.la - awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp1.la > 10_PlaceAndOrientContigs/tmp2.la + awk -f software/LepAnchor/scripts/propagate.awk {input.placed} > {output.iter1} + awk -f software/LepAnchor/scripts/propagate.awk {output.iter1} > {output.iter2} i=2 while ! cmp -s "10_PlaceAndOrientContigs/tmp$i.la" "10_PlaceAndOrientContigs/tmp$(( $i-1 )).la" ;do @@ -42,6 +44,6 @@ rule propogate2: done #create prop*.la - awk -f software/LepAnchor/scripts/propagate2.awk tmp$i.la | awk '(/^[^#]/ && NF>=8){{++d[$1"\t"($7+0)"\t"($8+0)]; data[++line]=$0}}END{{for (i=1; i<=line; ++i) {{$0=data[i];if (d[$1"\t"($7+0)"\t"($8+0)] == 1) {{fn="10_PlaceAndOrientContigs/propogate/propogated."$5".la";print $0>fn}}}}}}' + awk -f software/LepAnchor/scripts/propagate2.awk 10_PlaceAndOrientContigs/tmp$i.la | awk '(/^[^#]/ && NF>=8){{++d[$1"\t"($7+0)"\t"($8+0)]; data[++line]=$0}}END{{for (i=1; i<=line; ++i) {{$0=data[i];if (d[$1"\t"($7+0)"\t"($8+0)] == 1) {{fn="10_PlaceAndOrientContigs/propogate/propogated."$5".la";print $0>fn}}}}}}' awk '{{print $1"\t"($7+0)"\t"($8+0)"\t?\t"$5}}' {output.prop} | awk -f software/LepAnchor/scripts/pickbed.awk - {input.bedfile} > {output.propogated} """ \ No newline at end of file diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index 4df922d..ae7f45c 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -13,7 +13,7 @@ rule place_orient3: params: chrom = "{lg_range}", extras = place_orient_extra, - datatype = data_type, + datatype = data_type message: "Running 3rd round of PlaceAndOrientContigs for linkage group {params.chrom}" threads: 2 shell: @@ -46,7 +46,7 @@ rule liftoverHaplotypes: threads: 1 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.chrom} haplotypes={input.haplos} chain=- > $i.liftover + gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.chrom} haplotypes={input.haplos} chain=- > {output} """ rule removehaplotypes: diff --git a/rules/LepAnchor/place_orient4.smk b/rules/LepAnchor/place_orient4.smk index 03b9503..dd86e03 100644 --- a/rules/LepAnchor/place_orient4.smk +++ b/rules/LepAnchor/place_orient4.smk @@ -6,8 +6,7 @@ rule place_orient4: prox = proximity, lift = "10_PlaceAndOrientContigs/liftover.la", chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", - chromlast = "10_PlaceAndOrientContigs/orient_3/chr.{lg_range}.la", - liftover = expand("10_PlaceAndOrientContigs/liftover/chr.{lgs}.liftover", lgs = lg_range) + chromlast = "10_PlaceAndOrientContigs/orient_3/chr.{lg_range}.la" output: chrom = "10_PlaceAndOrientContigs/orient_4/chr.{lg_range}.la", err = "10_PlaceAndOrientContigs/orient_4/errors/chr.{lg_range}.errors" @@ -15,11 +14,11 @@ rule place_orient4: params: chrom = "{lg_range}", extras = place_orient_extra, - datatype = data_type, + datatype = data_type threads: 2 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map=$MAPL chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} """ rule prune_contigblocks: From 70b4e8b0e37b9f628349e541c32875a722f1decb Mon Sep 17 00:00:00 2001 From: "Pavel V. Dimens" Date: Sun, 6 Feb 2022 12:51:20 -0500 Subject: [PATCH 13/46] Update config.yml --- config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.yml b/config.yml index b65ff0f..afdc5be 100644 --- a/config.yml +++ b/config.yml @@ -126,9 +126,9 @@ extra_params_Map2Bed: "" #lepanchor_input: "noIntervals=0" # data is intervals lepanchor_input: "noIntervals=1" # data is distances -# The size limit for detecting potential haplotype contigs (default: ~5000) +# The size limit for detecting potential haplotype contigs (LepAnchor default: 2000) # Set this value really high (50000+) to ignore haplotype removal in between PlaceOrient iterations -haplotype_limit: 5000 +haplotype_limit: 2000 # Additional parameters you would like to use for PlaceAndOrientContigs, if any (e.g. randomOrder=1) extra_params_PlaceOrient: "keepEmptyIntervals=1 numRuns=10" From d6ab3d2dcafbc498f796b124b16ffd76e00b01cd Mon Sep 17 00:00:00 2001 From: "Pavel V. Dimens" Date: Sun, 6 Feb 2022 12:52:10 -0500 Subject: [PATCH 14/46] Update LepWrap --- LepWrap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LepWrap b/LepWrap index 8443514..2f80969 100755 --- a/LepWrap +++ b/LepWrap @@ -148,9 +148,9 @@ extra_params_Map2Bed: "" #lepanchor_input: "noIntervals=0" # data is intervals lepanchor_input: "noIntervals=1" # data is distances -# The size limit for detecting potential haplotype contigs (default: ~5000) +# The size limit for detecting potential haplotype contigs (LepAnchor default: 2000) # Set this value really high (50000+) to ignore haplotype removal in between PlaceOrient iterations -haplotype_limit: 5000 +haplotype_limit: 2000 # Additional parameters you would like to use for PlaceAndOrientContigs, if any (e.g. randomOrder=1) extra_params_PlaceOrient: "keepEmptyIntervals=1 numRuns=10" From 1ee7a46ea075c21134bef442dbf2ce840d9413f6 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 13:22:57 -0500 Subject: [PATCH 15/46] cleanup and sanity checks --- LepWrap | 86 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/LepWrap b/LepWrap index 2f80969..753f48d 100755 --- a/LepWrap +++ b/LepWrap @@ -1,5 +1,18 @@ #! /usr/bin/env bash +# Help text +if [[ -z "$1" ]]; then + echo "Perform the modules of Lep-Map3 and/or Lep-Anchor" + echo "Make sure the config file (default: config.yml) is properly configured for how you want to run things" + echo "The second positional argument is optional if your config file is named config.yml" + echo "" + echo "[usage] LepWrap <# of threads> " + echo "[example] LepWrap 16" + echo "[example] LepWrap 30 kosambi.20iterations.yml" + exit 1 +fi + +# Check for snakemake if which snakemake &>/dev/null; then foo=1 else @@ -11,15 +24,8 @@ else exit 1 fi -if [[ -z "$2" ]]; then - if [ ! -f config.yml ]; then - echo -n "Error: " - printf "\033[01;32m" - printf "config.yml" - printf "\033[0m" - echo " not found" - echo "The file 'config.yml' was created for you, please edit it and run LepWrap again" - cat <config.yml +function generateconfig { + cat <$1 # Configuration file for LepWrap #=======================================================# # Lep-Map 3 # @@ -163,36 +169,50 @@ extra_params_PlaceOrient: "keepEmptyIntervals=1 numRuns=10" # Set edge_length to the percent number of markers you would like to examine from either end of the linkage group # Value can be an integer or decimal, i.e. 15 is the same as 0.15, which both mean "15%" (10-15 is reasonable) -LA_edge_length: 20 +LA_edge_length: 15 -# Set trim_cuttoff to the centiMorgan distance cutoff (5 is reasonable) +# Set trim_cuttoff to the centiMorgan distance cutoff (5-10 is reasonable) LA_trim_cutoff: 5 EOF - exit 1 - fi - CONF=config.yml +} + +export -f generateconfig + +# make sure the first argument is a number (number of threads) +# function from https://stackoverflow.com/a/61835747 +is_int() { case ${1#[-+]} in '' | *[!0-9]* ) return 1;; esac ;} + +if $(is_int $1); then + foo=1 else - if [ ! -f $2 ]; then - echo -n "Error: Config file " - printf "\033[01;32m" - printf "$2" - printf "\033[0m" - echo " not found" - exit 1 - else - CONF=$2 - fi + echo "Error: the first argument must the number of threads, as an integer." + exit 1 fi -if [[ -z "$1" ]]; then - echo "Perform the modules of Lep-Map3 and/or Lep-Anchor" - echo "Make sure the config file (default: config.yml) is properly configured for how you want to run things" - echo "The second positional argument is optional if your config file is named config.yml" - echo "" - echo "[usage] LepWrap <# of threads> " - echo "[example] LepWrap 16" - echo "[example] LepWrap 30 kosambi.20iterations.yml" - exit 1 +if [[ ! -z "$2" ]]; then + if [ ! -f $2 ]; then + echo -n "Error: Config file " + printf "\033[01;32m" + printf "$2" + printf "\033[0m" + echo " was not found, but it was created for you. Please edit it and run LepWrap again" + generateconfig $2 + exit 1 + else + CONF=$2 + fi +else + if [ ! -f config.yml ]; then + echo -n "Error: " + printf "\033[01;32m" + printf "config.yml" + printf "\033[0m" + echo " not found" + echo " was not found, but it was created for you. Please edit it and run LepWrap again" + generateconfig config.yml + exit 1 + fi + CONF=config.yml fi lepmap(){ From ec7258f06b26682698f6a53a1b96fef00f6a5490 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 13:27:22 -0500 Subject: [PATCH 16/46] bind LM3 error to LA run. Nicer printing --- LepWrap | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/LepWrap b/LepWrap index 753f48d..4a18c97 100755 --- a/LepWrap +++ b/LepWrap @@ -216,13 +216,17 @@ else fi lepmap(){ - echo "Running Lep-Map3" + printf "\033[01;32m" + printf "Running Lep-Map3\n" + printf "\033[0m" sleep 2s snakemake --cores $1 --snakefile ./rules/LepMap3/LepMap3.smk --configfile $CONF --directory . } lepanchor(){ - echo -e "\nRunning Lep-Anchor" + printf "\033[01;32m" + printf "Running Lep-Anchor\n" + printf "\033[0m" sleep 2s snakemake --cores $1 --snakefile ./rules/LepAnchor/LepAnchor.smk --configfile $CONF --directory . } @@ -241,9 +245,7 @@ if [ $LM == "true" ]; then lepmap $1 $CONF else echo "Skipping Lep-Map3" -fi - -if [ $LA == "true" ]; then +fi && if [ $LA == "true" ]; then lepanchor $1 $CONF else echo "Skipping LepAnchor" From 638b2e26f7b902decae68b2bc89b9c6e231bde76 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 13:31:36 -0500 Subject: [PATCH 17/46] QoL --- LepWrap | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/LepWrap b/LepWrap index 4a18c97..5c37ce5 100755 --- a/LepWrap +++ b/LepWrap @@ -3,12 +3,21 @@ # Help text if [[ -z "$1" ]]; then echo "Perform the modules of Lep-Map3 and/or Lep-Anchor" - echo "Make sure the config file (default: config.yml) is properly configured for how you want to run things" + echo "The config file (default: config.yml) must be configured" echo "The second positional argument is optional if your config file is named config.yml" echo "" - echo "[usage] LepWrap <# of threads> " - echo "[example] LepWrap 16" - echo "[example] LepWrap 30 kosambi.20iterations.yml" + printf "\033[01;32m" + printf "[usage]" + printf "\033[0m" + echo " LepWrap <# of threads> " + printf "\033[01;32m" + printf "[example]" + printf "\033[0m" + echo " LepWrap 16" + printf "\033[01;32m" + printf "[example]" + printf "\033[0m" + echo " LepWrap 30 kosambi.20iterations.yml" exit 1 fi From f2c157d1bb7e9451e298d6395492731717e67777 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 14:06:11 -0500 Subject: [PATCH 18/46] housekeeping --- rules/LepAnchor/LA_extra.smk | 11 ---------- rules/LepAnchor/build_agp.smk | 7 +++---- rules/LepAnchor/build_fasta.smk | 32 ++++++++--------------------- rules/LepAnchor/generate_inputs.smk | 8 +++----- rules/LepAnchor/place_orient1.smk | 8 ++++---- rules/LepAnchor/place_orient2.smk | 9 ++++---- rules/LepAnchor/place_orient3.smk | 10 ++++----- rules/LepAnchor/place_orient4.smk | 10 ++++----- 8 files changed, 32 insertions(+), 63 deletions(-) delete mode 100644 rules/LepAnchor/LA_extra.smk diff --git a/rules/LepAnchor/LA_extra.smk b/rules/LepAnchor/LA_extra.smk deleted file mode 100644 index d98b15d..0000000 --- a/rules/LepAnchor/LA_extra.smk +++ /dev/null @@ -1,11 +0,0 @@ -rule remove_haplos: - input: - bedfile = "10_Anchoring/map_extra.bed", - haplotypes = "10_Anchoring/suspected.haplotypes.after" - output: "10_Anchoring/map.nohaplotypes.bed" - message: "Creating bedfile with suspected haplotypes removed" - shell: - """ - grep -w -v -f <(cut -f 2 {input.haplotypes}) {input.bedfile} > {output} - #awk -f software/LepAnchor/scripts/removeHaplotypes.awk {input.bedfile} {input.haplotypes} > {output}" - """ \ No newline at end of file diff --git a/rules/LepAnchor/build_agp.smk b/rules/LepAnchor/build_agp.smk index eb51e42..8a9116b 100644 --- a/rules/LepAnchor/build_agp.smk +++ b/rules/LepAnchor/build_agp.smk @@ -1,6 +1,5 @@ rule construct_agp: - input: - cleaned = "10_PlaceAndOrientContigs/overlaps.removed.la" + input: "10_PlaceAndOrientContigs/overlaps.removed.la" output: agp = report("11_AGP/contigs/chr.{lg_range}.agp", category = "Contig AGP Files"), scaff_agp = report("11_AGP/scaffolds/chr.{lg_range}.scaffolds.agp", category = "Scaffold AGP Files") @@ -9,8 +8,8 @@ rule construct_agp: chrom = "{lg_range}" shell: """ - awk -vn={params.chrom} '($5==n)' {input.cleaned} | awk -vprefix="LG" -vlg={params.chrom} -f software/LepAnchor/scripts/makeagp_full2.awk - > {output.agp} - awk -vn={params.chrom} '($5==n)' {input.cleaned} | awk -vprefix="LG" -vlg={params.chrom} -f software/LepAnchor/scripts/makeagp2.awk - > {output.scaff_agp} + awk -vn={params.chrom} '($5==n)' {input} | awk -vprefix="LG" -vlg={params.chrom} -f software/LepAnchor/scripts/makeagp_full2.awk - > {output.agp} + awk -vn={params.chrom} '($5==n)' {input} | awk -vprefix="LG" -vlg={params.chrom} -f software/LepAnchor/scripts/makeagp2.awk - > {output.scaff_agp} """ diff --git a/rules/LepAnchor/build_fasta.smk b/rules/LepAnchor/build_fasta.smk index 8aefe0a..c9fd642 100644 --- a/rules/LepAnchor/build_fasta.smk +++ b/rules/LepAnchor/build_fasta.smk @@ -2,49 +2,33 @@ rule build_scaffold_only_fasta: input: assembly = geno, agp = "11_AGP/lepanchor.contigs.only.agp" - output: - fasta = "12_Fasta/Anchored.scaffolds.only.fa.gz", + output: "12_Fasta/Anchored.scaffolds.only.fa.gz" message: "Constructing final scaffold-only fasta file {output.fasta}" - shell: - """ - gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.agp} | gzip > {output.fasta} - """ + shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.agp} | gzip > {output}" rule build_scaffold_contig_fasta: input: assembly = geno, agp = "11_AGP/lepanchor.contigs.all.agp" - output: - fasta = "12_Fasta/Anchored.scaffolds.fa.gz", + output: "12_Fasta/Anchored.scaffolds.fa.gz" message: "Constructing final scaffold fasta file {output.fasta}" - shell: - """ - gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.agp} | gzip > {output.fasta} - """ + shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.agp} | gzip > {output}" rule build_contig_only_fasta: input: assembly = geno, scaff_agp = "11_AGP/lepanchor.scaffolds.only.agp" - output: - fasta = "12_Fasta/Anchored.contigs.only.fa.gz" + output: "12_Fasta/Anchored.contigs.only.fa.gz" message: "Constructing final contig-only fasta file {output.fasta}" - shell: - """ - gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.scaff_agp} | gzip > {output.fasta} - """ + shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.scaff_agp} | gzip > {output}" rule build_contig_fasta: input: assembly = geno, scaff_agp = "11_AGP/lepanchor.scaffolds.all.agp" - output: - fasta = "12_Fasta/Anchored.contigs.fa.gz" + output: "12_Fasta/Anchored.contigs.fa.gz" message: "Constructing final contig fasta file {output.fasta}" - shell: - """ - gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.scaff_agp} | gzip > {output.fasta} - """ \ No newline at end of file + shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.scaff_agp} | gzip > {output}" \ No newline at end of file diff --git a/rules/LepAnchor/generate_inputs.smk b/rules/LepAnchor/generate_inputs.smk index affe53d..8dfb335 100644 --- a/rules/LepAnchor/generate_inputs.smk +++ b/rules/LepAnchor/generate_inputs.smk @@ -8,7 +8,7 @@ rule extract_markers: rule generate_input_data: input: markers = "snps.txt", - data = expand("7_Intervals/ordered.{x}.intervals", x = range(1, lg + 1)) if data_type == "noIntervals=0" else expand("7_Distances/ordered.{x}.distances", x = range(1, lg + 1)) + data = expand("7_Intervals/ordered.{x}.intervals", x = lg_range) if data_type == "noIntervals=0" else expand("7_Distances/ordered.{x}.distances", x = lg_range) output: data = report("10_PlaceAndOrientContigs/lepanchor.input", category = "Data") message: "Combining {params} Lep-Map3 files into single LepAnchor input {output}" @@ -38,10 +38,7 @@ rule find_haplotypes: input: "9_Chain/chainfile.gz" output: report("10_PlaceAndOrientContigs/suspected.haplotypes.initial", category = "Logs") message: "Finding non-haplotype contigs not included in map.bed" - shell: - """ - gunzip -fc {input} | awk -f software/LepAnchor/scripts/findFullHaplotypes.awk > {output} - """ + shell: "gunzip -fc {input} | awk -f software/LepAnchor/scripts/findFullHaplotypes.awk > {output}" rule liftover: @@ -69,6 +66,7 @@ rule cleanmap: extras = cleanmap_extra shell: "java -cp software/LepAnchor CleanMap map={input} {params.extras} > {output} 2> {log}" + rule map2bed: input: cleanmap = "10_PlaceAndOrientContigs/map_all.clean", diff --git a/rules/LepAnchor/place_orient1.smk b/rules/LepAnchor/place_orient1.smk index f25d197..eb21666 100644 --- a/rules/LepAnchor/place_orient1.smk +++ b/rules/LepAnchor/place_orient1.smk @@ -6,8 +6,8 @@ rule place_orient: prox = proximity, lift = "10_PlaceAndOrientContigs/liftover.la" output: - chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", - chromerr = report("10_PlaceAndOrientContigs/orient_1/logs/chr.{lg_range}.la.log", category = "Anchoring I Logs") + chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la", + chromerr = "10_PlaceAndOrientContigs/1_orient/logs/chr.{lg_range}.la.log" params: chrom = "{lg_range}", extras = place_orient_extra, @@ -21,8 +21,8 @@ rule place_orient: rule propogate1: input: - placed = expand("10_PlaceAndOrientContigs/orient_1/chr.{lgs}.la", lgs = lg_range), - errors = expand("10_PlaceAndOrientContigs/orient_1/logs/chr.{lgs}.la.log", lgs = lg_range), + placed = expand("10_PlaceAndOrientContigs/1_orient/chr.{lgs}.la", lgs = lg_range), + errors = expand("10_PlaceAndOrientContigs/1_orient/logs/chr.{lgs}.la.log", lgs = lg_range), bedfile = "10_PlaceAndOrientContigs/map_extra.bed" output: propogated = "10_PlaceAndOrientContigs/map.propogated.bed", diff --git a/rules/LepAnchor/place_orient2.smk b/rules/LepAnchor/place_orient2.smk index 000934f..912b9ba 100644 --- a/rules/LepAnchor/place_orient2.smk +++ b/rules/LepAnchor/place_orient2.smk @@ -5,10 +5,10 @@ rule place_orient2: paf = paf, prox = proximity, lift = "10_PlaceAndOrientContigs/liftover.la", - chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la" + chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la" output: - chrom = "10_PlaceAndOrientContigs/orient_2/chr.{lg_range}.la", - chromerr = "10_PlaceAndOrientContigs/orient_2/logs/chr.{lg_range}.err" + chrom = "10_PlaceAndOrientContigs/2_orient/chr.{lg_range}.la", + chromerr = "10_PlaceAndOrientContigs/2_orient/logs/chr.{lg_range}.err" params: chrom = "{lg_range}", extras = place_orient_extra, @@ -22,8 +22,7 @@ rule place_orient2: rule propogate2: input: - placed = expand("10_PlaceAndOrientContigs/orient_2/chr.{lgs}.la", lgs = lg_range), - #placed = expand("10_PlaceAndOrientContigs/orient_2/logs/chr.{lgs}.err", lgs = lg_range), + placed = expand("10_PlaceAndOrientContigs/2_orient/chr.{lgs}.la", lgs = lg_range), bedfile = "10_PlaceAndOrientContigs/map.propogated.bed" output: prop = expand("10_PlaceAndOrientContigs/propogate/propogated.{lgs}.la", lgs = lg_range), diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index ae7f45c..7ab8ffa 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -5,11 +5,11 @@ rule place_orient3: paf = paf, prox = proximity, lift = "10_PlaceAndOrientContigs/liftover.la", - chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", + chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la", propogated = "10_PlaceAndOrientContigs/propogate/propogated.{lg_range}.la" output: - chrom = "10_PlaceAndOrientContigs/orient_3/chr.{lg_range}.la", - errors = "10_PlaceAndOrientContigs/orient_3/errors/chr.{lg_range}.err" + chrom = "10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la", + errors = "10_PlaceAndOrientContigs/3_orient/errors/chr.{lg_range}.err" params: chrom = "{lg_range}", extras = place_orient_extra, @@ -23,7 +23,7 @@ rule place_orient3: rule find_haplotypes2: input: - errors = expand("10_PlaceAndOrientContigs/orient_3/errors/chr.{lgs}.err", lgs = lg_range) + errors = expand("10_PlaceAndOrientContigs/3_orient/errors/chr.{lgs}.err", lgs = lg_range) output: haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" message: "Finding full haplotypes" @@ -39,7 +39,7 @@ rule find_haplotypes2: rule liftoverHaplotypes: input: chain = "9_Chain/chainfile.gz", - chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", + chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la", haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" output: "10_PlaceAndOrientContigs/liftover/chr.{lg_range}.liftover" message: "Running liftoverHaplotypes for {input.chrom}" diff --git a/rules/LepAnchor/place_orient4.smk b/rules/LepAnchor/place_orient4.smk index dd86e03..0928e9c 100644 --- a/rules/LepAnchor/place_orient4.smk +++ b/rules/LepAnchor/place_orient4.smk @@ -5,11 +5,11 @@ rule place_orient4: paf = paf, prox = proximity, lift = "10_PlaceAndOrientContigs/liftover.la", - chrom = "10_PlaceAndOrientContigs/orient_1/chr.{lg_range}.la", - chromlast = "10_PlaceAndOrientContigs/orient_3/chr.{lg_range}.la" + chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la", + chromlast = "10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la" output: - chrom = "10_PlaceAndOrientContigs/orient_4/chr.{lg_range}.la", - err = "10_PlaceAndOrientContigs/orient_4/errors/chr.{lg_range}.errors" + chrom = "10_PlaceAndOrientContigs/4_orient/chr.{lg_range}.la", + err = "10_PlaceAndOrientContigs/4_orient/errors/chr.{lg_range}.errors" message: "Running 4th round of PlaceAndOrientContigs for linkage group {params.chrom}" params: chrom = "{lg_range}", @@ -22,7 +22,7 @@ rule place_orient4: """ rule prune_contigblocks: - input: "10_PlaceAndOrientContigs/orient_4/chr.{lg_range}.la" + input: "10_PlaceAndOrientContigs/4_orient/chr.{lg_range}.la" output: chrom = "10_PlaceAndOrientContigs/pruned/chr.{lg_range}.pruned.la", err = temp("10_PlaceAndOrientContigs/pruned/err/chr.{lg_range}.pruned.err") From a556c4ee41cbe6fad426fd10d7aa37bffaa4d287 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 14:19:44 -0500 Subject: [PATCH 19/46] restore my changes --- software/LepAnchor/scripts/plot_marey.R | 108 ++++++++++++------------ 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/software/LepAnchor/scripts/plot_marey.R b/software/LepAnchor/scripts/plot_marey.R index 3cd3ced..8bf6538 100644 --- a/software/LepAnchor/scripts/plot_marey.R +++ b/software/LepAnchor/scripts/plot_marey.R @@ -1,55 +1,57 @@ -library(scales) +#! /usr/bin/env Rscript +# Written by Pasi Rastas (2021) +# modified by Pavel Dimens for Lep-Wrap (2021) -map=read.table(gzfile("marey.data.gz")) - -NN=unique(map$V3) -MM=unique(map$V4) - -cl=rainbow(length(MM)) - - -for(lg in NN) { - print(lg) - png(paste0("marey", lg, ".png"),width=1024,height=1024) - ymax = 0 - xmax = 0 - for (m in MM) { - map_tmp = map[map$V1==paste0("LG",lg) & map$V3==lg & map$V4==m,] - if (nrow(map_tmp) > 0) { - ymax = max(ymax, max(map_tmp$V5)) - xmax = max(xmax, max(map_tmp$V2)) - } - } - - index = 1 - for (m in MM) { - map_tmp = map[map$V1==paste0("LG",lg) & map$V3==lg & map$V4==m,] - if (nrow(map_tmp) > 0) { - orientation=glm(map_tmp$V2 ~ map_tmp$V5)$coefficients[2] - if (!is.na(orientation) && orientation < 0) { - map_tmp$V5 = max(map_tmp$V6) - map_tmp$V5 - map_tmp$V6 = max(map_tmp$V6) - map_tmp$V6 - } - } - if (index == 1) { - plot(map_tmp$V2, map_tmp$V5, xlab="Position (Mb)",ylab="Recombination Distance",xaxt="n", main=paste0("LG", lg), col=alpha(cl[index],0.3), pch=20, cex=1.0, ylim=c(0,ymax), xlim=c(0,xmax)) - points(map_tmp$V2, map_tmp$V6, col=alpha(cl[index],0.3), pch=20, cex=1.0) - arrows(map_tmp$V2, map_tmp$V5, map_tmp$V2, map_tmp$V6, length=0.0, col=alpha(cl[index],0.5),lwd=1.5) - } - else { - points(map_tmp$V2, map_tmp$V5, col=alpha(cl[index],0.3), pch=20, cex=1.0) - points(map_tmp$V2, map_tmp$V6, col=alpha(cl[index],0.3), pch=20, cex=1.0) - arrows(map_tmp$V2, map_tmp$V5, map_tmp$V2, map_tmp$V6, length=0.0, col=alpha(cl[index],0.5),lwd=1.5) - } - index = index + 1 - } - - agp <- read.table(paste0("chr", lg, ".agp")) - agp=agp[agp$V1==paste0("LG",lg) & agp$V5=="W",c(1:3)] - - segments(agp$V2,2*c(1:2),agp$V3,2*c(1:2),col=c("red","blue"),lwd=2,lend=1) - axis(1,seq(0,500000000,5000000),seq(0,500,5)) - segments(agp$V2,0,agp$V2,ymax,lwd=0.5,col=rgb(0.5,0.5,0.5,0.2)) - dev.off() -} +library(scales) +args <- commandArgs(trailingOnly = TRUE) + +# args[1] = path/to/marey.data.gz +# args[2] = directory/of/chrom AGP files + +map <- read.table(gzfile(args[1])) + +basedir <- dirname(args[1]) + +NN <- unique(map$V3) +MM <- unique(map$V4) + +cl <- rainbow(length(MM)) + +for (lg in NN) { + #print(lg) + png(paste0(basedir, "/LG.", lg ,".mareymap", ".png"), width = 1024, height = 1024) + ymax <- 0 + for (m in MM) { + map_tmp <- map[map$V3 == lg & map$V4 == m, ] + ymax <- max(ymax, max(map_tmp$V5)) + } + + index <- 1 + for (m in MM) { + map_tmp <- map[map$V3 == lg & map$V4 == m, ] + if (glm(map_tmp$V2 ~ map_tmp$V5)$coefficients[2] < 0) { + map_tmp$V5 <- max(map_tmp$V5) - map_tmp$V5 + map_tmp$V6 <- max(map_tmp$V6) - map_tmp$V6 + } + if (index == 1) { + plot(map_tmp$V2, map_tmp$V5, xlab = "Position (Mb)", ylab = "Recombination Distance (cM)", xaxt = "n", main = paste0("LG", lg), col = alpha(cl[index], 0.3), pch = 20, cex = 1.0, ylim = c(0, ymax)) + points(map_tmp$V2, map_tmp$V6, col = alpha(cl[index], 0.3), pch = 20, cex = 1.0) + arrows(map_tmp$V2, map_tmp$V5, map_tmp$V2, map_tmp$V6, length = 0.0, col = alpha(cl[index], 0.5), lwd = 1.5) + } + else { + points(map_tmp$V2, map_tmp$V5, col = alpha(cl[index], 0.3), pch = 20, cex = 1.0) + points(map_tmp$V2, map_tmp$V6, col = alpha(cl[index], 0.3), pch = 20, cex = 1.0) + arrows(map_tmp$V2, map_tmp$V5, map_tmp$V2, map_tmp$V6, length = 0.0, col = alpha(cl[index], 0.5), lwd = 1.5) + } + index <- index + 1 + } + + agp <- read.table(paste0(normalizePath(args[2]), "/chr.", lg, ".agp")) + agp <- agp[agp$V1 == paste0("LG", lg) & agp$V5 == "W", c(1:3)] + + segments(agp$V2, 2 * c(1:2), agp$V3, 2 * c(1:2), col = c("red", "blue"), lwd = 2, lend = 1) + axis(1, seq(0, 500000000, 5000000), seq(0, 500, 5)) + segments(agp$V2, 0, agp$V2, ymax, lwd = 0.5, col = rgb(0.5, 0.5, 0.5, 0.2)) + dev.off() +} \ No newline at end of file From 106c0861c33d745ac67d18904b56f5d25b0ad210 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 15:21:55 -0500 Subject: [PATCH 20/46] consolidate LAtrim and LepWrapTrim --- rules/LepAnchor/trim_edges.smk | 2 +- scripts/LATrim.r | 162 --------------------------------- scripts/LepWrapTrim.r | 45 ++++++--- 3 files changed, 33 insertions(+), 176 deletions(-) delete mode 100755 scripts/LATrim.r diff --git a/rules/LepAnchor/trim_edges.smk b/rules/LepAnchor/trim_edges.smk index 627c025..f6d2372 100644 --- a/rules/LepAnchor/trim_edges.smk +++ b/rules/LepAnchor/trim_edges.smk @@ -19,7 +19,7 @@ rule trim_newintervals: params: edge = edgelen, dist = trimdist - shell: "Rscript scripts/LATrim.r {input} {params.dist} {params.edge} 15_Trim" + shell: "Rscript scripts/LepWrapTrim.r {input} {params.dist} {params.edge} 15_Trim" rule merge_trimplots: diff --git a/scripts/LATrim.r b/scripts/LATrim.r deleted file mode 100755 index c4581d8..0000000 --- a/scripts/LATrim.r +++ /dev/null @@ -1,162 +0,0 @@ -#! /usr/bin/env Rscript - -suppressMessages(if (!require("tidyverse")) install.packages("tidyverse")) -suppressMessages(library("tidyverse")) -suppressMessages(if (!require("cowplot")) install.packages("cowplot")) -library(cowplot) - -args <- commandArgs(trailingOnly = TRUE) -# args[1] is the OrderMarkers2 output file -# args[2] is the centimorgan cutoff distance -# args[3] is the % of edge markers to scan -# args[4] is the name of the output folder - -lgfile <- read.delim( - args[1], - header = FALSE, - sep = "\t", - comment.char="#" -) %>% - mutate(Mpass = T, Fpass = T) - -## setup output file names ## -# split the filename by path -filename <- unlist(strsplit(args[1], "/")) -# pop out just the filename -filename <- filename[length(filename)] -lg <- (strsplit(lgfile[1,1], "LG") %>% unlist())[2] %>% as.numeric() - -#========= output instantiation ========# - -dir.create(args[4], showWarnings = FALSE) -dir.create(paste0(args[4],"/plots"), showWarnings = FALSE) -dir.create(paste0(args[4],"/logs"), showWarnings = FALSE) -dir.create(paste0(args[4],"/QC_raw"), showWarnings = FALSE) -outfile_base <- paste(args[4], filename, sep = "/") -outfile_log_base <- paste(args[4], "logs", filename, sep = "/") -plotfile_base <- paste(args[4], "plots", filename, sep = "/") -plotfile <- paste(plotfile_base, "trim.pdf", sep = ".") -rawfile_base <- paste(args[4], "QC_raw", filename, sep = "/") - -##### Pruning the ends ##### -dist_thresh <- as.numeric(args[2]) -if(dist_thresh >= 1){ - dist_thresh <- dist_thresh * .01 -} - -dist_thresh_all <- c( - abs(max(lgfile[, 5]) - min(lgfile[, 5])) * dist_thresh, # male - abs(max(lgfile[, 6]) - min(lgfile[, 6])) * dist_thresh # female -) - -# if the percent threshold is given as an integer, convert it to a decimal -edge_length <- as.numeric(args[3]) -if(edge_length >= 1){ - edge_length <- edge_length * .01 -} -n_markers <- nrow(lgfile) -forward_start <- round(n_markers * edge_length, digits = 0) -reverse_start <- round(n_markers - forward_start, digits = 0) - -for (j in 5:6){ # iterate over male (5) and female (6) - # sort on column - lgfile <- arrange(lgfile, j) - dist_thresh <- dist_thresh_all[j-4] - # trim beginning - # the loop goes towards the edges to be effecient with the break() call, removing the - # entire cluster once one bad marker is found - for(a in forward_start:2){ #first n% of total markers starting from the forward edge, going out - diff <- abs(lgfile[a,j]-lgfile[a-1,j]) # difference between two points - if( diff > dist_thresh ){ # is the difference between the two points > distance argument? - lgfile[(a-1):1, j+2] <- FALSE # mark that marker and all markers BEFORE it as FAIL - break() - } - } - # trim end - for(z in reverse_start:(n_markers-1)){ #last n% total markers starting from the reverse edge going out - diff <- abs(lgfile[z+1,j]-lgfile[z,j]) # difference between two points - if( diff > dist_thresh ){ # is the difference between the two points > distance argument? - lgfile[(z+1):n_markers,j+2] <- FALSE # mark that marker and all markers AFTER it as FAIL - break() - } - } -} - -# create new table of markers passing QC -cleaned_markers <- (lgfile %>% filter(Mpass & Fpass))[,1:6] -# re-scale cleaned markers to 0 by subtracting the minimum genetic position -cleaned_markers <- cleaned_markers %>% - mutate(V5 = V5 - min(V5), V6 = V6 - min(V6)) - -# isolate bad markers -removed_markers <- (lgfile %>% filter(!Mpass | !Fpass))[,1:6] - -# get simple counts -rm_male <- lgfile %>% filter(!Mpass & Fpass) %>% nrow() -rm_female <- lgfile %>% filter(!Fpass & Mpass) %>% nrow() -rm_both <- lgfile %>% filter(!Mpass & !Fpass) %>% nrow() - -pdf(NULL) - -plot_male <- lgfile %>% arrange(V5) %>% - ggplot(aes(x = seq_along(V5), y = V5, color = Mpass)) + - geom_point(shape = 19) + - scale_color_manual(values = c("dodgerblue", "indianred2"), limits = c(T, F)) + - geom_vline(xintercept = forward_start, linetype = "dashed", size = 0.2) + - geom_vline(xintercept = reverse_start, linetype = "dashed", size = 0.2) + - labs( - title = "", - subtitle = paste0(rm_male, " male markers >", dist_thresh_all[1], "cM trimmed"), - caption = paste0(edge_length, "% edge markers, ", dist_thresh, "% cM"), - x = "Marker Number", - y = "Position (cM)", - color = "Pass Filtering" - ) - -plot_female <- lgfile %>% arrange(V6) %>% - ggplot(aes(x = seq_along(V6), y = V6, color = Fpass)) + - geom_point(shape = 19) + - scale_color_manual(values = c("dodgerblue", "indianred2"), limits = c(T, F)) + - geom_vline(xintercept = forward_start, linetype = "dashed", size = 0.2) + - geom_vline(xintercept = reverse_start, linetype = "dashed", size = 0.2) + - labs( - title = paste("Edge Cluster Trimming for LG:", lg), - subtitle = paste0(rm_female, " female markers >", dist_thresh_all[2], "cM trimmed"), - caption = paste0("Markers failing both M+F: ", rm_both), - x = "Marker Number", - y = "Position (cM)", - color = "Pass Filtering", - legend.position = "none" - ) - -plot_grid(plot_female, plot_male, ncol = 2, nrow = 1) - -suppressMessages(ggsave(plotfile, width = 7, height = 3, units = "in")) - -write.table( - cleaned_markers, - file = paste(outfile_base, "trimmed", sep = "."), - sep = "\t", - quote = FALSE, - row.names = FALSE, - col.names = FALSE, -) - -write.table( - lgfile, - file = paste(rawfile_base, "filtered.raw", sep = "."), - sep = "\t", - quote = FALSE, - row.names = FALSE, - col.names = FALSE, -) - -write.table( - removed_markers, - file=paste(outfile_log_base, "removed", sep = "."), - append=FALSE, - sep = "\t", - quote = FALSE, - row.names = FALSE, - col.names = FALSE -) \ No newline at end of file diff --git a/scripts/LepWrapTrim.r b/scripts/LepWrapTrim.r index 6a6e17a..d1c0166 100755 --- a/scripts/LepWrapTrim.r +++ b/scripts/LepWrapTrim.r @@ -4,7 +4,7 @@ suppressMessages(if (!require("tidyverse")) install.packages("tidyverse")) suppressMessages(library("tidyverse")) args <- commandArgs(trailingOnly = TRUE) -# args[1] is the OrderMarkers2 output file +# args[1] is the OrderMarkers2/LepAnchor output file # args[2] is the centiMorgan cutoff threshold # args[3] is the % of edge markers to scan # args[4] is the output directory @@ -39,12 +39,27 @@ lgfile <- read.delim( ) %>% mutate(Mpass = T, Fpass = T) +# Modify which columns to look at based on input format +# LM3 input has 5 columns + 2 from M|Fpass +# LA input has 6 columns + 2 from M|Fpass +if(ncol(lgfile) == 7) { + # LepMap3 input file + idxcol <- c(2,3) + keepcol <- 1:5 + lgidx <- 2 +} else { + # LepAnchor input file + idxcol <- c(5, 6) + keepcol <- 1:6 + lgidx <- 3 +} + ## setup output file names ## # split the filename by path filename <- unlist(strsplit(args[1], "/")) # pop out just the filename filename <- filename[length(filename)] -lg <- unlist(strsplit(filename, "\\."))[2] +lg <- unlist(strsplit(filename, "\\."))[lgidx] #========= instantiate output ========# dir.create(args[4], showWarnings = FALSE) @@ -66,8 +81,8 @@ if(dist_thresh >= 1){ } dist_thresh_all <- c( - abs(max(lgfile[, 2]) - min(lgfile[, 2])) * dist_thresh, # male - abs(max(lgfile[, 3]) - min(lgfile[, 3])) * dist_thresh # female + abs(max(lgfile[, idxcol[1]]) - min(lgfile[, idxcol[1]])) * dist_thresh, # male + abs(max(lgfile[, idxcol[2]]) - min(lgfile[, idxcol[2]])) * dist_thresh # female ) edge_length <- as.numeric(args[3]) @@ -80,10 +95,11 @@ forward_start <- round(n_markers * edge_length, digits = 0) reverse_start <- round(n_markers - forward_start, digits = 0) # iterate over male (2) and female (3) -for (j in 2:3){ +threshidx <- 1 +for (j in idxcol){ # sort on column lgfile <- arrange(lgfile, j) - dist_thresh <- dist_thresh_all[j-1] + dist_thresh <- dist_thresh_all[threshidx] # trim beginning for(a in forward_start:2){ #first n% of total markers from the beginning diff <- abs(lgfile[a,j]-lgfile[a-1,j]) # difference between two points @@ -100,6 +116,7 @@ for (j in 2:3){ break() } } + threshidx <- threshidx + 1 } # isolate bad markers @@ -115,7 +132,7 @@ rm_both <- lgfile %>% filter(!Mpass & !Fpass) %>% nrow() pdf(NULL) plot_df <- lgfile %>% - rename(Male = V2, Female = V3) %>% + rename(Male = idxcol[1], Female = idxcol[2]) %>% arrange(Male) %>% mutate(Marker = seq_along(Mpass)) %>% rowwise() %>% @@ -126,15 +143,17 @@ plot_df <- lgfile %>% mutate(Fail = QAfix(Fail, Sex)) plot_df %>% - ggplot(aes(Marker, Position, color = Fail)) + - geom_point(shape = 19, alpha = 0.3) + + ggplot(aes(Marker, Position, color = Fail, shape = Fail)) + + geom_point(alpha = 0.5) + scale_color_manual(values = c("dodgerblue", "indianred2"), limits = c(T, F)) + + scale_shape_manual(values = c(19, 17), limits = c(T, F), guide = "none") + geom_vline(xintercept = forward_start, linetype = "dashed", size = 0.2) + geom_vline(xintercept = reverse_start, linetype = "dashed", size = 0.2) + + guides(color = guide_legend(override.aes = list(size = c(2,2), shape = c(19, 17)))) + labs( title = paste("Edge Cluster Trimming Results for Linkage Group", lg), subtitle = paste0("Markers Failing QC: ", rm_female, " female, ", rm_male, " male, ", rm_both, " both (", rm_female+rm_male+rm_both, " total)" ), - caption = paste0(edge_length*100, "% of edge markers, ", args[2], "% cM threshold: ", dist_thresh_all[2], "(F) & ", dist_thresh_all[1], "(M)"), + caption = paste0(edge_length*100, "% of edge markers, ", args[2], "% cM threshold: ", round(dist_thresh_all[2], digits = 2), "(F) & ", round(dist_thresh_all[1], digits = 2), "(M)"), x = "Marker Number", y = "Position (cM)", color = "Pass QA" @@ -151,14 +170,14 @@ fail_idx <- which(!lgfile$QC) # prepend a comment to the flagged markers so LepMap3 ignores them lgfile[fail_idx, 1] <- paste0("#", lgfile[fail_idx, 1]) # re-scale remaining markers to 0 by subtracting the minimum genetic position for each sex -lgfile[,2] <- round(lgfile[,2] - (min(lgfile[lgfile$QC,2])), digits = 3) -lgfile[,3] <- round(lgfile[,3] - (min(lgfile[lgfile$QC,3])), digits = 3) +lgfile[,idxcol[1]] <- round(lgfile[,idxcol[1]] - (min(lgfile[lgfile$QC,idxcol[1]])), digits = 3) +lgfile[,idxcol[2]] <- round(lgfile[,idxcol[2]] - (min(lgfile[lgfile$QC,idxcol[2]])), digits = 3) # write header to a new file writeLines(readLines(args[1], n=2), con = paste(outfile_base, "trimmed", sep = ".")) # write the remainder to that file write.table( - lgfile[,1:5], + lgfile[, keepcol], file = paste(outfile_base, "trimmed", sep = "."), sep = "\t", quote = FALSE, From 821a2a7cb8489c769d01b9471f40078291cf3eef Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 15:24:57 -0500 Subject: [PATCH 21/46] pedantic codefix --- LepWrap | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/LepWrap b/LepWrap index 5c37ce5..62173a3 100755 --- a/LepWrap +++ b/LepWrap @@ -4,8 +4,7 @@ if [[ -z "$1" ]]; then echo "Perform the modules of Lep-Map3 and/or Lep-Anchor" echo "The config file (default: config.yml) must be configured" - echo "The second positional argument is optional if your config file is named config.yml" - echo "" + echo -e "The second positional argument is optional if your config file is named config.yml\n" printf "\033[01;32m" printf "[usage]" printf "\033[0m" @@ -17,7 +16,7 @@ if [[ -z "$1" ]]; then printf "\033[01;32m" printf "[example]" printf "\033[0m" - echo " LepWrap 30 kosambi.20iterations.yml" + echo -e " LepWrap 30 kosambi.20iterations.yml" exit 1 fi From 9d649c137843eafd417393f36aca3b0be1783141 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 15:33:05 -0500 Subject: [PATCH 22/46] fix legacy code --- LepWrap | 2 +- rules/LepAnchor/build_fasta.smk | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/LepWrap b/LepWrap index 62173a3..ca90068 100755 --- a/LepWrap +++ b/LepWrap @@ -193,7 +193,7 @@ is_int() { case ${1#[-+]} in '' | *[!0-9]* ) return 1;; esac ;} if $(is_int $1); then foo=1 else - echo "Error: the first argument must the number of threads, as an integer." + echo "Error: the first argument must be the number of threads, as an integer." exit 1 fi diff --git a/rules/LepAnchor/build_fasta.smk b/rules/LepAnchor/build_fasta.smk index c9fd642..4e9d7b5 100644 --- a/rules/LepAnchor/build_fasta.smk +++ b/rules/LepAnchor/build_fasta.smk @@ -3,7 +3,7 @@ rule build_scaffold_only_fasta: assembly = geno, agp = "11_AGP/lepanchor.contigs.only.agp" output: "12_Fasta/Anchored.scaffolds.only.fa.gz" - message: "Constructing final scaffold-only fasta file {output.fasta}" + message: "Constructing final scaffold-only fasta file {output}" shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.agp} | gzip > {output}" @@ -12,7 +12,7 @@ rule build_scaffold_contig_fasta: assembly = geno, agp = "11_AGP/lepanchor.contigs.all.agp" output: "12_Fasta/Anchored.scaffolds.fa.gz" - message: "Constructing final scaffold fasta file {output.fasta}" + message: "Constructing final scaffold fasta file {output}" shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.agp} | gzip > {output}" @@ -21,7 +21,7 @@ rule build_contig_only_fasta: assembly = geno, scaff_agp = "11_AGP/lepanchor.scaffolds.only.agp" output: "12_Fasta/Anchored.contigs.only.fa.gz" - message: "Constructing final contig-only fasta file {output.fasta}" + message: "Constructing final contig-only fasta file {output}" shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.scaff_agp} | gzip > {output}" @@ -30,5 +30,5 @@ rule build_contig_fasta: assembly = geno, scaff_agp = "11_AGP/lepanchor.scaffolds.all.agp" output: "12_Fasta/Anchored.contigs.fa.gz" - message: "Constructing final contig fasta file {output.fasta}" + message: "Constructing final contig fasta file {output}" shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.scaff_agp} | gzip > {output}" \ No newline at end of file From 7dabf47c145dd3532783fb60aa7fb31d93ee4089 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 15:33:39 -0500 Subject: [PATCH 23/46] add missing message --- rules/LepAnchor/trim_edges.smk | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/LepAnchor/trim_edges.smk b/rules/LepAnchor/trim_edges.smk index f6d2372..e00a10e 100644 --- a/rules/LepAnchor/trim_edges.smk +++ b/rules/LepAnchor/trim_edges.smk @@ -39,4 +39,5 @@ rule merge_trimmedintervals: rule plot_trimmedintervals: input: "16_MareyMapsTrimmed/data.marey.trimmed.gz" output: report("16_MareyMapsTrimmed/LepAnchor.mareymaps.pdf", category = "Trimmed Marey Maps") + message: "Plotting results of edge trimming" shell: "Rscript scripts/LASummary.r {input}" \ No newline at end of file From ab4ee98229d2421a5358b03a163bce6bba7b423d Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Sun, 6 Feb 2022 19:05:53 -0500 Subject: [PATCH 24/46] fix plot script --- scripts/LepWrapTrim.r | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/scripts/LepWrapTrim.r b/scripts/LepWrapTrim.r index d1c0166..d4c6290 100755 --- a/scripts/LepWrapTrim.r +++ b/scripts/LepWrapTrim.r @@ -39,6 +39,12 @@ lgfile <- read.delim( ) %>% mutate(Mpass = T, Fpass = T) +## setup output file names ## +# split the filename by path +filename <- unlist(strsplit(args[1], "/")) +# pop out just the filename +filename <- filename[length(filename)] + # Modify which columns to look at based on input format # LM3 input has 5 columns + 2 from M|Fpass # LA input has 6 columns + 2 from M|Fpass @@ -46,21 +52,16 @@ if(ncol(lgfile) == 7) { # LepMap3 input file idxcol <- c(2,3) keepcol <- 1:5 - lgidx <- 2 + lg <- unlist(strsplit(filename, "\\."))[2] + colshift <- 4 } else { # LepAnchor input file idxcol <- c(5, 6) keepcol <- 1:6 - lgidx <- 3 + lg <- unlist(strsplit(filename, "\\."))[3] + colshift <- 2 } -## setup output file names ## -# split the filename by path -filename <- unlist(strsplit(args[1], "/")) -# pop out just the filename -filename <- filename[length(filename)] -lg <- unlist(strsplit(filename, "\\."))[lgidx] - #========= instantiate output ========# dir.create(args[4], showWarnings = FALSE) dir.create(paste0(args[4],"/plots"), showWarnings = FALSE) @@ -104,7 +105,7 @@ for (j in idxcol){ for(a in forward_start:2){ #first n% of total markers from the beginning diff <- abs(lgfile[a,j]-lgfile[a-1,j]) # difference between two points if( diff > dist_thresh ){ # is the difference between the two points > distance argument? - lgfile[(a-1):1, j+4] <- FALSE # all markers BEFORE it as FAIL + lgfile[(a-1):1, j + colshift] <- FALSE # all markers BEFORE it as FAIL break() } } @@ -112,7 +113,7 @@ for (j in idxcol){ for(z in reverse_start:(n_markers-1)){ #last n% total markers starting from the back edge going out diff <- abs(lgfile[z+1,j]-lgfile[z,j]) # difference between two points if( diff > dist_thresh ){ # is the difference between the two points > distance argument? - lgfile[(z+1):n_markers,j+4] <- FALSE # all markers AFTER it as FAIL + lgfile[(z+1):n_markers, j + colshift] <- FALSE # all markers AFTER it as FAIL break() } } From 44fe78f6454580cab1f35995d93be7fff94d4a61 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Tue, 8 Feb 2022 20:03:05 -0500 Subject: [PATCH 25/46] omit place iteration 4 --- rules/LepAnchor/LepAnchor.smk | 2 +- rules/LepAnchor/place_orient3.smk | 97 ++++++++++++++++++++----------- rules/LepAnchor/place_orient4.smk | 3 +- 3 files changed, 64 insertions(+), 38 deletions(-) diff --git a/rules/LepAnchor/LepAnchor.smk b/rules/LepAnchor/LepAnchor.smk index e15c3fe..c318868 100644 --- a/rules/LepAnchor/LepAnchor.smk +++ b/rules/LepAnchor/LepAnchor.smk @@ -24,7 +24,7 @@ include: "mask_and_chain.smk" include: "place_orient1.smk" include: "place_orient2.smk" include: "place_orient3.smk" -include: "place_orient4.smk" +#include: "place_orient4.smk" include: "build_agp.smk" include: "build_fasta.smk" include: "mareymaps_untrimmed.smk" diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index 7ab8ffa..ad820af 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -21,40 +21,67 @@ rule place_orient3: gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} """ -rule find_haplotypes2: - input: - errors = expand("10_PlaceAndOrientContigs/3_orient/errors/chr.{lgs}.err", lgs = lg_range) - output: - haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" - message: "Finding full haplotypes" - params: - haplo = haplo_limit - shell: - """ - awk '($NF=="haplotype")' {input.errors} | - sort -n -r | - awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} - """ +rule prune_contigblocks: + input: "expand(10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la") + output: + chrom = "10_PlaceAndOrientContigs/pruned/chr.{lg_range}.pruned.la", + err = "10_PlaceAndOrientContigs/pruned/err/chr.{lg_range}.pruned.err" + message: "Pruning contig blocks without map support and removing overlaps" + params: + chrom = lg + shell: "awk -f software/LepAnchor/scripts/prune.awk {input} > {output.chrom} 2> {output.err}" -rule liftoverHaplotypes: - input: - chain = "9_Chain/chainfile.gz", - chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la", - haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" - output: "10_PlaceAndOrientContigs/liftover/chr.{lg_range}.liftover" - message: "Running liftoverHaplotypes for {input.chrom}" - threads: 1 - shell: - """ - gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.chrom} haplotypes={input.haplos} chain=- > {output} - """ +rule prune_post: + input: + bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", + prunedchrom = expand("10_PlaceAndOrientContigs/pruned/chr.{lgs}.pruned.la", lgs = lg_range), + prunederr = expand("10_PlaceAndOrientContigs/pruned/err/chr.{lgs}.pruned.err", lgs = lg_range) + output: + overlaps = "10_PlaceAndOrientContigs/overlaps.removed.la", + pruned = "10_PlaceAndOrientContigs/pruned.la" + message: "Removing overlaps" + threads: 1 + shell: + """ + cat {input.prunederr} > {output.pruned} + awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} + """ -rule removehaplotypes: - input: - mapfile = "10_PlaceAndOrientContigs/map.propogated2.bed", - haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" - output: - bedfile = "10_PlaceAndOrientContigs/map.propogated2.nohaplo.bed" - message: "Removing haplotypes from the map" - threads: 1 - shell: "awk -f software/LepAnchor/scripts/removeHaplotypes.awk {input} > {output}" +#rule find_haplotypes2: +# input: +# errors = expand("10_PlaceAndOrientContigs/3_orient/errors/chr.{lgs}.err", lgs = lg_range) +# output: +# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" +# message: "Finding full haplotypes" +# params: +# haplo = haplo_limit +# shell: +# """ +# awk '($NF=="haplotype")' {input.errors} | +# sort -n -r | +# awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} +# """ +# +#rule liftoverHaplotypes: +# input: +# chain = "9_Chain/chainfile.gz", +# chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la", +# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" +# output: "10_PlaceAndOrientContigs/liftover/chr.{lg_range}.liftover" +# message: "Running liftoverHaplotypes for {input.chrom}" +# threads: 1 +# shell: +# """ +# gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.chrom} haplotypes={input.haplos} chain=- > {output} +# """ +# +#rule removehaplotypes: +# input: +# mapfile = "10_PlaceAndOrientContigs/map.propogated2.bed", +# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" +# output: +# bedfile = "10_PlaceAndOrientContigs/map.propogated2.nohaplo.bed" +# message: "Removing haplotypes from the map" +# threads: 1 +# shell: "awk -f software/LepAnchor/scripts/removeHaplotypes.awk {input} > {output}" +# \ No newline at end of file diff --git a/rules/LepAnchor/place_orient4.smk b/rules/LepAnchor/place_orient4.smk index 0928e9c..f7c0f40 100644 --- a/rules/LepAnchor/place_orient4.smk +++ b/rules/LepAnchor/place_orient4.smk @@ -22,10 +22,9 @@ rule place_orient4: """ rule prune_contigblocks: - input: "10_PlaceAndOrientContigs/4_orient/chr.{lg_range}.la" + input: "expand(10_PlaceAndOrientContigs/4_orient/chr.{lgs}.la", lgs = lg_range) output: chrom = "10_PlaceAndOrientContigs/pruned/chr.{lg_range}.pruned.la", - err = temp("10_PlaceAndOrientContigs/pruned/err/chr.{lg_range}.pruned.err") message: "Pruning contig blocks without map support and removing overlaps" params: chrom = lg From bcbf9d4809b4a6934147c04381c0fe689a1e2660 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Tue, 8 Feb 2022 20:06:31 -0500 Subject: [PATCH 26/46] fix indentation --- rules/LepAnchor/place_orient3.smk | 44 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index ad820af..8c736fe 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -22,30 +22,30 @@ rule place_orient3: """ rule prune_contigblocks: - input: "expand(10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la") - output: - chrom = "10_PlaceAndOrientContigs/pruned/chr.{lg_range}.pruned.la", - err = "10_PlaceAndOrientContigs/pruned/err/chr.{lg_range}.pruned.err" - message: "Pruning contig blocks without map support and removing overlaps" - params: - chrom = lg - shell: "awk -f software/LepAnchor/scripts/prune.awk {input} > {output.chrom} 2> {output.err}" + input: "expand(10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la") + output: + chrom = "10_PlaceAndOrientContigs/pruned/chr.{lg_range}.pruned.la", + err = "10_PlaceAndOrientContigs/pruned/err/chr.{lg_range}.pruned.err" + message: "Pruning contig blocks without map support and removing overlaps" + params: + chrom = lg + shell: "awk -f software/LepAnchor/scripts/prune.awk {input} > {output.chrom} 2> {output.err}" rule prune_post: - input: - bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", - prunedchrom = expand("10_PlaceAndOrientContigs/pruned/chr.{lgs}.pruned.la", lgs = lg_range), - prunederr = expand("10_PlaceAndOrientContigs/pruned/err/chr.{lgs}.pruned.err", lgs = lg_range) - output: - overlaps = "10_PlaceAndOrientContigs/overlaps.removed.la", - pruned = "10_PlaceAndOrientContigs/pruned.la" - message: "Removing overlaps" - threads: 1 - shell: - """ - cat {input.prunederr} > {output.pruned} - awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} - """ + input: + bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", + prunedchrom = expand("10_PlaceAndOrientContigs/pruned/chr.{lgs}.pruned.la", lgs = lg_range), + prunederr = expand("10_PlaceAndOrientContigs/pruned/err/chr.{lgs}.pruned.err", lgs = lg_range) + output: + overlaps = "10_PlaceAndOrientContigs/overlaps.removed.la", + pruned = "10_PlaceAndOrientContigs/pruned.la" + message: "Removing overlaps" + threads: 1 + shell: + """ + cat {input.prunederr} > {output.pruned} + awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} + """ #rule find_haplotypes2: # input: From f7c1115a9afcdda02de762c247e658396efcff88 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Tue, 8 Feb 2022 20:07:43 -0500 Subject: [PATCH 27/46] remove expand call --- rules/LepAnchor/place_orient3.smk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index 8c736fe..8193be7 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -22,7 +22,7 @@ rule place_orient3: """ rule prune_contigblocks: - input: "expand(10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la") + input: "10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la" output: chrom = "10_PlaceAndOrientContigs/pruned/chr.{lg_range}.pruned.la", err = "10_PlaceAndOrientContigs/pruned/err/chr.{lg_range}.pruned.err" From b8029573a07131e00be2500699b475b308f73321 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Tue, 8 Feb 2022 20:24:11 -0500 Subject: [PATCH 28/46] fix unused contig finding --- rules/LepAnchor/build_agp.smk | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rules/LepAnchor/build_agp.smk b/rules/LepAnchor/build_agp.smk index 8a9116b..0854114 100644 --- a/rules/LepAnchor/build_agp.smk +++ b/rules/LepAnchor/build_agp.smk @@ -12,23 +12,21 @@ rule construct_agp: awk -vn={params.chrom} '($5==n)' {input} | awk -vprefix="LG" -vlg={params.chrom} -f software/LepAnchor/scripts/makeagp2.awk - > {output.scaff_agp} """ - rule unused: input: lengths = "10_PlaceAndOrientContigs/contigs.length", - haplos = "10_PlaceAndOrientContigs/suspected.haplotypes", - cleaned = "10_PlaceAndOrientContigs/overlaps.removed.la", + haplos = "10_PlaceAndOrientContigs/suspected.haplotypes.initial", + agp = expand("11_AGP/contigs/chr.{lgs}.agp", lgs = lg_range) output: - txt = "11_AGP/not_used_final.txt", + txt = "11_AGP/not_used.txt", agp = "11_AGP/not_used.agp" message: "Finding unused contigs" shell: """ - cut -f 1 {input.lengths} | grep -v -w -F -f <(cut -f2 {input.haplos}; cut -f1 {input.cleaned}) > {output.txt} + cut -f 1 {input.lengths} | grep -v -w -F -f <(cut -f 2 {input.haplos}; awk '($5!="U"){{print $6}}' {input.agp}) > {output.txt} grep -F -w -f {output.txt} {input.lengths} | awk '{{print $1,1,$2,1,"W",$1,1,$2,"+"}}' > {output.agp} """ - rule build_final_agp: input: agp = expand("11_AGP/contigs/chr.{lgs}.agp", lgs = lg_range), From d542e59417886e7ad417148c32879ba1af7512a1 Mon Sep 17 00:00:00 2001 From: "Pavel V. Dimens" Date: Mon, 14 Feb 2022 11:34:50 -0500 Subject: [PATCH 29/46] Create install.sh --- .misc/install.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .misc/install.sh diff --git a/.misc/install.sh b/.misc/install.sh new file mode 100644 index 0000000..344251a --- /dev/null +++ b/.misc/install.sh @@ -0,0 +1,18 @@ +# install LepWrap into conda PATH +mkdir -p $CONDA_PREFIX/bin +# LepWrap executable +cp LepWrap $CONDA_PREFIX/bin/ +chmod +x $CONDA_PREFIX/bin/LepWrap +# associated scripts +chmod +x scripts/* +cp scripts/* $CONDA_PREFIX/bin/ +# LepMap3 modules and scripts +cp software/LepMap3/*.class $CONDA_PREFIX/bin +cp software/LepMap3/scripts/* $CONDA_PREFIX/bin +# LepAnchor modules and scripts +cp software/LepAnchor/*.class software/LepAnchor/lepanchor_wrapper.sh $CONDA_PREFIX/bin +cp software/LepAnchor/scripts/* $CONDA_PREFIX/bin +cp software/LepAnchor/deps/ucsc_binaries/* $CONDA_PREFIX/bin +cp software/LepAnchor/deps/*.pl software/LepAnchor/deps/Red software/LepAnchor/deps/all_lastz.ctl software/LepAnchor/deps/scoreMatrix.q software/LepAnchor/deps/step* $CONDA_PREFIX/bin +# Snakemake rules +cp rules/LepAnchor/*.smk rules/LepMap3/*.smk $CONDA_PREFIX/bin From 7bcabadacc5287b496b3ae8e07ddd5d3f3a2ca01 Mon Sep 17 00:00:00 2001 From: "Pavel V. Dimens" Date: Mon, 14 Feb 2022 11:35:30 -0500 Subject: [PATCH 30/46] Create build.sh --- .misc/build.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .misc/build.sh diff --git a/.misc/build.sh b/.misc/build.sh new file mode 100644 index 0000000..1d0a5ad --- /dev/null +++ b/.misc/build.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# for installing conda package +mkdir -p $PREFIX/bin +# LepWrap executable +cp LepWrap $PREFIX/bin/ +chmod +x $PREFIX/bin/LepWrap +# associated scripts +chmod +x scripts/* +cp scripts/* $PREFIX/bin/ +# LepMap3 modules and scripts +cp software/LepMap3/*.class $PREFIX/bin +cp software/LepMap3/scripts/* $PREFIX/bin +# LepAnchor modules and scripts +cp software/LepAnchor/*.class software/LepAnchor/lepanchor_wrapper.sh $PREFIX/bin +cp software/LepAnchor/scripts/* $PREFIX/bin +cp software/LepAnchor/deps/ucsc_binaries/* $PREFIX/bin +cp software/LepAnchor/deps/*.pl software/LepAnchor/deps/Red software/LepAnchor/deps/all_lastz.ctl software/LepAnchor/deps/scoreMatrix.q software/LepAnchor/deps/step* $PREFIX/bin +# Snakemake rules +cp rules/LepAnchor/*.smk rules/LepMap3/*.smk $PREFIX/bin From 50cb858481e3e7559dd6842b302db7bc74527767 Mon Sep 17 00:00:00 2001 From: "Pavel V. Dimens" Date: Mon, 14 Feb 2022 11:35:48 -0500 Subject: [PATCH 31/46] Update install.sh --- .misc/install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.misc/install.sh b/.misc/install.sh index 344251a..6cbc7b5 100644 --- a/.misc/install.sh +++ b/.misc/install.sh @@ -1,3 +1,5 @@ +#! /usr/bin/env bash + # install LepWrap into conda PATH mkdir -p $CONDA_PREFIX/bin # LepWrap executable From c1a86455c2dca48dc0a9499555b9cf027c7a5d7e Mon Sep 17 00:00:00 2001 From: "Pavel V. Dimens" Date: Mon, 14 Feb 2022 11:36:06 -0500 Subject: [PATCH 32/46] Update build.sh --- .misc/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.misc/build.sh b/.misc/build.sh index 1d0a5ad..608c20a 100644 --- a/.misc/build.sh +++ b/.misc/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#! usr/bin/env bash # for installing conda package mkdir -p $PREFIX/bin # LepWrap executable From e100c558d1970bc0ec0ff9d699e3410a9ad8ca22 Mon Sep 17 00:00:00 2001 From: "Pavel V. Dimens" Date: Mon, 14 Feb 2022 11:36:52 -0500 Subject: [PATCH 33/46] Create meta.yml --- .misc/meta.yml | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .misc/meta.yml diff --git a/.misc/meta.yml b/.misc/meta.yml new file mode 100644 index 0000000..26e8868 --- /dev/null +++ b/.misc/meta.yml @@ -0,0 +1,45 @@ +{% set version = "3.6.2" %} +{% set sha256 = "0f0998ecd50b586d69b50e53d9a0b3d5aa3afa70bd96fe85c2fa3ba02e58278b" %} + +package: + name: lepwrap + version: '{{ version }}' + +source: + url: https://github.com/pdimens/LepWrap/archive/refs/tags/{{ version }}.tar.gz + sha256: {{ sha256 }} + +build: + number: 3 + noarch: generic + +requirements: + build: + host: + run: + - bzip2 + - font-ttf-dejavu-sans-mono + - font-ttf-ubuntu + - pygraphviz + - graphviz + - imagemagick + - openjdk + - pandoc + - python >=3.9 + - r-base >=4 + - r-tidyverse + - sed + - snakemake >=6.4 + +test: + commands: + - "java --version" + - "R --version" +about: + home: "https://github.com/pdimens/LepWrap/" + license: "The GNU General Public License v3.0 (GPL3)" + summary: "The Snakemake pipeline to use Lep-Map3 to create linkage maps and LepAnchor for anchoring+orienting genome assemblies." + +extra: + container: + extended-base: True From bfae878a432b9f382d9f87a3463d477b19781823 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Mon, 14 Feb 2022 12:26:31 -0500 Subject: [PATCH 34/46] first attempt at consolidation --- .misc/build.sh | 3 +- .misc/install.sh | 2 +- HM.all_lastz.log | 0 HM.axtChainRecipBestNet.log | 1 + HM.initiation.log | 1 + HM2.all_lastz.log | 3 + LepWrap | 170 +----------------------- rules/LepAnchor/build_agp.smk | 4 +- rules/LepAnchor/build_fasta.smk | 8 +- rules/LepAnchor/generate_inputs.smk | 10 +- rules/LepAnchor/mareymaps_untrimmed.smk | 10 +- rules/LepAnchor/mask_and_chain.smk | 22 ++- rules/LepAnchor/place_orient1.smk | 4 +- rules/LepAnchor/place_orient2.smk | 12 +- rules/LepAnchor/place_orient3.smk | 12 +- rules/LepAnchor/place_orient4.smk | 6 +- rules/LepAnchor/trim_edges.smk | 4 +- rules/LepMap3/distances.smk | 4 +- rules/LepMap3/generate_map.smk | 6 +- rules/LepMap3/order.smk | 7 +- rules/LepMap3/prepare_data.smk | 4 +- rules/LepMap3/reorder.smk | 4 +- rules/LepMap3/trim.smk | 6 +- scripts/FilterLinkageMap.r | 2 + scripts/RecombinationSummary.r | 1 + scripts/generate_config.sh | 151 +++++++++++++++++++++ scripts/generate_lastzctl.sh | 48 +++++++ scripts/generate_qscoremtx.sh | 9 ++ scripts/iterate_js2all.sh | 2 +- scripts/refinemap.sh | 2 +- scripts/usage | 4 +- thingsx.seq | 1 + thingsx.sizes | 1 + x.seq | 1 + x.sizes | 1 + 35 files changed, 299 insertions(+), 227 deletions(-) create mode 100644 HM.all_lastz.log create mode 100644 HM.axtChainRecipBestNet.log create mode 100644 HM.initiation.log create mode 100644 HM2.all_lastz.log create mode 100755 scripts/generate_config.sh create mode 100755 scripts/generate_lastzctl.sh create mode 100755 scripts/generate_qscoremtx.sh create mode 120000 thingsx.seq create mode 120000 thingsx.sizes create mode 120000 x.seq create mode 120000 x.sizes diff --git a/.misc/build.sh b/.misc/build.sh index 608c20a..2ba015f 100644 --- a/.misc/build.sh +++ b/.misc/build.sh @@ -1,4 +1,5 @@ #! usr/bin/env bash + # for installing conda package mkdir -p $PREFIX/bin # LepWrap executable @@ -12,7 +13,7 @@ cp software/LepMap3/*.class $PREFIX/bin cp software/LepMap3/scripts/* $PREFIX/bin # LepAnchor modules and scripts cp software/LepAnchor/*.class software/LepAnchor/lepanchor_wrapper.sh $PREFIX/bin -cp software/LepAnchor/scripts/* $PREFIX/bin +cp $CONDA_PREFIX/bin/* $PREFIX/bin cp software/LepAnchor/deps/ucsc_binaries/* $PREFIX/bin cp software/LepAnchor/deps/*.pl software/LepAnchor/deps/Red software/LepAnchor/deps/all_lastz.ctl software/LepAnchor/deps/scoreMatrix.q software/LepAnchor/deps/step* $PREFIX/bin # Snakemake rules diff --git a/.misc/install.sh b/.misc/install.sh index 6cbc7b5..d87f015 100644 --- a/.misc/install.sh +++ b/.misc/install.sh @@ -13,7 +13,7 @@ cp software/LepMap3/*.class $CONDA_PREFIX/bin cp software/LepMap3/scripts/* $CONDA_PREFIX/bin # LepAnchor modules and scripts cp software/LepAnchor/*.class software/LepAnchor/lepanchor_wrapper.sh $CONDA_PREFIX/bin -cp software/LepAnchor/scripts/* $CONDA_PREFIX/bin +cp $CONDA_PREFIX/bin/* $CONDA_PREFIX/bin cp software/LepAnchor/deps/ucsc_binaries/* $CONDA_PREFIX/bin cp software/LepAnchor/deps/*.pl software/LepAnchor/deps/Red software/LepAnchor/deps/all_lastz.ctl software/LepAnchor/deps/scoreMatrix.q software/LepAnchor/deps/step* $CONDA_PREFIX/bin # Snakemake rules diff --git a/HM.all_lastz.log b/HM.all_lastz.log new file mode 100644 index 0000000..e69de29 diff --git a/HM.axtChainRecipBestNet.log b/HM.axtChainRecipBestNet.log new file mode 100644 index 0000000..9efda7c --- /dev/null +++ b/HM.axtChainRecipBestNet.log @@ -0,0 +1 @@ +Can't open perl script "../software/LepAnchor/deps/HM_axtChainRecipBestNet.pl": No such file or directory diff --git a/HM.initiation.log b/HM.initiation.log new file mode 100644 index 0000000..1134612 --- /dev/null +++ b/HM.initiation.log @@ -0,0 +1 @@ +Can't open perl script "../software/LepAnchor/deps/initiation.pl": No such file or directory diff --git a/HM2.all_lastz.log b/HM2.all_lastz.log new file mode 100644 index 0000000..7b3c223 --- /dev/null +++ b/HM2.all_lastz.log @@ -0,0 +1,3 @@ +Can't open perl script "../software/LepAnchor/deps/HM_all_lastz_mThreads.pl": No such file or directory +Can't open perl script "../software/LepAnchor/deps/HM_all_lastz_mThreads.pl": No such file or directory +Can't open perl script "../software/LepAnchor/deps/HM_all_lastz_mThreads.pl": No such file or directory diff --git a/LepWrap b/LepWrap index ca90068..2b78c49 100755 --- a/LepWrap +++ b/LepWrap @@ -32,160 +32,6 @@ else exit 1 fi -function generateconfig { - cat <$1 -# Configuration file for LepWrap -#=======================================================# -# Lep-Map 3 # -#=======================================================# -# Change this to false if you want to skip Lep-Map3 -run_lepmap: true - - #----- ParentCall2 ------# -# The filtered VCF file with your genotype likelihoods: -vcf: "mydata.vcf" - -# Instructions to create pedigree file: https://sourceforge.net/p/lep-map3/wiki/software/LepMap3 Home/#parentcall2 -# the pedigree file associated with your data -pedigree: "pedigree.txt" - -# Additional parameters for ParentCall2 (e.g. halfSibs=1), if any -extra_params_ParentCall: "removeNonInformative=1" - - - #----- Filtering2 -----# -# Data tolerance value-- set this to 0 if you want to skip the Filtering2 module -data_tol: 0 - -# Additional parameters for Filtering2 (e.g. convert2Biallelic=1), if any -extra_params_Filtering: "" - - - #----- SeperateChromosomes2 -----# -# LepWrap will iteratively perform SeperateChromosomes2 for each -# LOD score in the range of lod_min to lod_max - -# The minimum LOD for SeperateChromosomes2 -lod_min: 10 - -# The maximum LOD for SeperateChromosomes2 -lod_max: 50 - -# Use only markers with informative father (1), mother(2), both parents(3) or neither parent(0) -informative: "informativeMask=3" - -# Additional parameters for SeparateChromosomes2 (e.g. distrotionLOD=1), if any -extra_params_SeparateChromosomes: "sizeLimit=5 distortionLod=1" - - - #----- JoinSingles2ALL -----# -# Set this to false if you want to skip joining singles (0) to linkage groups -run_joinsingles2all: false - -# These are the parameters for JoinSingles2ALL, and are highly data-dependent -# Start with lower values for lod_limit and increase as necessary -lod_limit: "lodLimit=2" - -# Start with lower values for lod_limit and increase as necessary -lod_difference: "lodDifference=2" - -# Additional parameters for JoinSingles2All (e.g. iterate=1), if any -extra_params_JoinSingles: "iterate=1 distortionLod=1" - - - #----- OrderMarkers2 -----# -# Set exp_lg to your expected number of chromosomes for iterative ordering -exp_lg: 24 - -# Additional parameters for OrderMarkers2 (e.g. hyperPhaser=1), if any -# I recommend setting numMergeIterations to ~100 (Lep-Map3 default is 6) -extra_params_OrderMarkers: "useKosambi=1 phasingIterations=2 numMergeIterations=100" - - - #----- Edge Trimming -----# -# Edge trimming will examine the first and last X% of markers in a linkage group -# and remove clusters that are N% centimorgans (of the total cM span) away from -# the next marker. You can "skip" trimming by setting trim_cutoff really high (e.g. 80-100). - -# Set edge_length to the percent number of markers you would like to examine from either end of the linkage group -# Value can be an integer or decimal, i.e. 15 is the same as 0.15, which both mean "15%" (10-20 is reasonable) -edge_length: 20 - -# Set trim_cuttoff to the centiMorgan distance cutoff (5-10 is reasonable) -trim_cutoff: 100 - - - #----- Re-OrderMarkers2 -----# -# The second round of OrderMarkers will use the same basic parameters as the first round (but not the extra params) -# If there are additional parameters you would like to use, add them here: -extra_params_reOrderMarkers: "improveOrder=1 useKosambi=1 numMergeIterations=75" - - #----- Calculate Distances -----# -# If you used useKosambi=1 or useMorgan=1 for Ordering/reOrdering, add that same -# parameter to distance_method, otherwise leave it as a blank string -distance_method: "useKosambi=1" - - -#=======================================================# -# Lep-Anchor # -#=======================================================# - #---- global settings ----# -# Change this to false if you want to skip Lep-Anchor -run_lepanchor: true - -# The path to the genome assembly you are trying to anchor -assembly: "assembly.fasta" - -# The number of linkage groups you have -lg_count: 24 - -# If you have a PAF file of long reads mapped to your genome, add it here, otherwise leave the text as "/dev/null" -PAF_file: "/dev/null" - -# If you have a proximity file add it here, otherwise leave the text as "/dev/null". -# This isn't yet implemented in Lep-Anchor. -proximity_file: "/dev/null" - - - #----- CleanMap -----# -# Additional parameters for CleanMap (e.g. chimericDistance=500), if any -extra_params_CleanMap: "" - - - #----- Map2Bed -----# -# Additional parameters for Map2Bed (e.g. markerSupport=4), if any -extra_params_Map2Bed: "" - - - #----- PlaceAndOrientContigs -----# -# Choose which of the input types you want to generate by leaving it uncommented. Intervals are the default, but either works. -#lepanchor_input: "noIntervals=0" # data is intervals -lepanchor_input: "noIntervals=1" # data is distances - -# The size limit for detecting potential haplotype contigs (LepAnchor default: 2000) -# Set this value really high (50000+) to ignore haplotype removal in between PlaceOrient iterations -haplotype_limit: 2000 - -# Additional parameters you would like to use for PlaceAndOrientContigs, if any (e.g. randomOrder=1) -extra_params_PlaceOrient: "keepEmptyIntervals=1 numRuns=10" - - - #----- Edge Trimming -----# -# Edge trimming will examine the first and last X% of markers in a linkage group -# and remove clusters that are N% centimorgans (of the total cM span) away from -# the next marker. You can "skip" trimming by setting LA_trim_cutoff really high (e.g. 80-100) - -# Set edge_length to the percent number of markers you would like to examine from either end of the linkage group -# Value can be an integer or decimal, i.e. 15 is the same as 0.15, which both mean "15%" (10-15 is reasonable) -LA_edge_length: 15 - -# Set trim_cuttoff to the centiMorgan distance cutoff (5-10 is reasonable) -LA_trim_cutoff: 5 -EOF -} - -export -f generateconfig - # make sure the first argument is a number (number of threads) # function from https://stackoverflow.com/a/61835747 is_int() { case ${1#[-+]} in '' | *[!0-9]* ) return 1;; esac ;} @@ -204,7 +50,7 @@ if [[ ! -z "$2" ]]; then printf "$2" printf "\033[0m" echo " was not found, but it was created for you. Please edit it and run LepWrap again" - generateconfig $2 + generate_config.sh > $2 exit 1 else CONF=$2 @@ -217,7 +63,7 @@ else printf "\033[0m" echo " not found" echo " was not found, but it was created for you. Please edit it and run LepWrap again" - generateconfig config.yml + generate_config.sh > config.yml exit 1 fi CONF=config.yml @@ -228,7 +74,7 @@ lepmap(){ printf "Running Lep-Map3\n" printf "\033[0m" sleep 2s - snakemake --cores $1 --snakefile ./rules/LepMap3/LepMap3.smk --configfile $CONF --directory . + snakemake --cores $1 --snakefile $CONDA_PREFIX/bin/LepMap3.smk --configfile $CONF --directory . } lepanchor(){ @@ -236,14 +82,12 @@ lepanchor(){ printf "Running Lep-Anchor\n" printf "\033[0m" sleep 2s - snakemake --cores $1 --snakefile ./rules/LepAnchor/LepAnchor.smk --configfile $CONF --directory . + snakemake --cores $1 --snakefile $CONDA_PREFIX/bin/LepAnchor.smk --configfile $CONF --directory . } -# copy lastz binaries into conda path if they aren't already there if [ -z "$CONDA_PREFIX" ]; then - echo "No active conda environment detected" -else - cp -n software/LepAnchor/deps/ucsc_binaries/* $CONDA_PREFIX/bin + echo "No active conda environment detected. The LepWrap conda environment is required for the pipeline to work." + exit 1 fi LM=$(grep "run_lepmap" config.yml | cut -d":" -f2 | xargs | tr '[:upper:]' '[:lower:]') @@ -256,5 +100,5 @@ else fi && if [ $LA == "true" ]; then lepanchor $1 $CONF else - echo "Skipping LepAnchor" + echo "Skipping Lep-Anchor" fi diff --git a/rules/LepAnchor/build_agp.smk b/rules/LepAnchor/build_agp.smk index 0854114..983d57a 100644 --- a/rules/LepAnchor/build_agp.smk +++ b/rules/LepAnchor/build_agp.smk @@ -8,8 +8,8 @@ rule construct_agp: chrom = "{lg_range}" shell: """ - awk -vn={params.chrom} '($5==n)' {input} | awk -vprefix="LG" -vlg={params.chrom} -f software/LepAnchor/scripts/makeagp_full2.awk - > {output.agp} - awk -vn={params.chrom} '($5==n)' {input} | awk -vprefix="LG" -vlg={params.chrom} -f software/LepAnchor/scripts/makeagp2.awk - > {output.scaff_agp} + awk -vn={params.chrom} '($5==n)' {input} | awk -vprefix="LG" -vlg={params.chrom} -f $CONDA_PREFIX/bin/makeagp_full2.awk - > {output.agp} + awk -vn={params.chrom} '($5==n)' {input} | awk -vprefix="LG" -vlg={params.chrom} -f $CONDA_PREFIX/bin/makeagp2.awk - > {output.scaff_agp} """ rule unused: diff --git a/rules/LepAnchor/build_fasta.smk b/rules/LepAnchor/build_fasta.smk index 4e9d7b5..9d20133 100644 --- a/rules/LepAnchor/build_fasta.smk +++ b/rules/LepAnchor/build_fasta.smk @@ -4,7 +4,7 @@ rule build_scaffold_only_fasta: agp = "11_AGP/lepanchor.contigs.only.agp" output: "12_Fasta/Anchored.scaffolds.only.fa.gz" message: "Constructing final scaffold-only fasta file {output}" - shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.agp} | gzip > {output}" + shell: "gunzip -fc {input.assembly} | awk -f $CONDA_PREFIX/bin/makefasta.awk - {input.agp} | gzip > {output}" rule build_scaffold_contig_fasta: @@ -13,7 +13,7 @@ rule build_scaffold_contig_fasta: agp = "11_AGP/lepanchor.contigs.all.agp" output: "12_Fasta/Anchored.scaffolds.fa.gz" message: "Constructing final scaffold fasta file {output}" - shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.agp} | gzip > {output}" + shell: "gunzip -fc {input.assembly} | awk -f $CONDA_PREFIX/bin/makefasta.awk - {input.agp} | gzip > {output}" rule build_contig_only_fasta: @@ -22,7 +22,7 @@ rule build_contig_only_fasta: scaff_agp = "11_AGP/lepanchor.scaffolds.only.agp" output: "12_Fasta/Anchored.contigs.only.fa.gz" message: "Constructing final contig-only fasta file {output}" - shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.scaff_agp} | gzip > {output}" + shell: "gunzip -fc {input.assembly} | awk -f $CONDA_PREFIX/bin/makefasta.awk - {input.scaff_agp} | gzip > {output}" rule build_contig_fasta: @@ -31,4 +31,4 @@ rule build_contig_fasta: scaff_agp = "11_AGP/lepanchor.scaffolds.all.agp" output: "12_Fasta/Anchored.contigs.fa.gz" message: "Constructing final contig fasta file {output}" - shell: "gunzip -fc {input.assembly} | awk -f software/LepAnchor/scripts/makefasta.awk - {input.scaff_agp} | gzip > {output}" \ No newline at end of file + shell: "gunzip -fc {input.assembly} | awk -f $CONDA_PREFIX/bin/makefasta.awk - {input.scaff_agp} | gzip > {output}" \ No newline at end of file diff --git a/rules/LepAnchor/generate_inputs.smk b/rules/LepAnchor/generate_inputs.smk index 8dfb335..f1e3aa8 100644 --- a/rules/LepAnchor/generate_inputs.smk +++ b/rules/LepAnchor/generate_inputs.smk @@ -31,14 +31,14 @@ rule contiglengths: input: geno output: report("10_PlaceAndOrientContigs/contigs.length", category = "Data") message: "Getting contig lengths" - shell: "gunzip -fc {input} | awk -f software/LepAnchor/scripts/contigLength.awk > {output}" + shell: "gunzip -fc {input} | awk -f $CONDA_PREFIX/bin/contigLength.awk > {output}" rule find_haplotypes: input: "9_Chain/chainfile.gz" output: report("10_PlaceAndOrientContigs/suspected.haplotypes.initial", category = "Logs") message: "Finding non-haplotype contigs not included in map.bed" - shell: "gunzip -fc {input} | awk -f software/LepAnchor/scripts/findFullHaplotypes.awk > {output}" + shell: "gunzip -fc {input} | awk -f $CONDA_PREFIX/bin/findFullHaplotypes.awk > {output}" rule liftover: @@ -52,7 +52,7 @@ rule liftover: message: "Running liftoverHaplotypes for the input maps" shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.intervals} haplotypes={input.haplos} chain=- > {output.lift} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ LiftoverHaplotypes map={input.intervals} haplotypes={input.haplos} chain=- > {output.lift} cat {output.lift} | sort -V -k 1,1 -k 2,2n > {output.sortedlift} """ @@ -64,7 +64,7 @@ rule cleanmap: message: "Running CleanMap" params: extras = cleanmap_extra - shell: "java -cp software/LepAnchor CleanMap map={input} {params.extras} > {output} 2> {log}" + shell: "java -cp $CONDA_PREFIX/bin/ CleanMap map={input} {params.extras} > {output} 2> {log}" rule map2bed: @@ -76,7 +76,7 @@ rule map2bed: message: "Running Map2Bed" params: extras = map2bed_extra - shell: "java -cp software/LepAnchor Map2Bed map={input.cleanmap} contigLength={input.lengths} {params.extras} > {output} 2> {log}" + shell: "java -cp $CONDA_PREFIX/bin/ Map2Bed map={input.cleanmap} contigLength={input.lengths} {params.extras} > {output} 2> {log}" rule ungrouped: diff --git a/rules/LepAnchor/mareymaps_untrimmed.smk b/rules/LepAnchor/mareymaps_untrimmed.smk index bf93a90..d285a5b 100644 --- a/rules/LepAnchor/mareymaps_untrimmed.smk +++ b/rules/LepAnchor/mareymaps_untrimmed.smk @@ -19,12 +19,12 @@ rule mareymap_data: """ for c in $(seq 1 {params.chrom}) do - awk -vn=$c '($3==n)' {input.lift} | awk -f software/LepAnchor/scripts/liftover.awk 11_AGP/contigs/chr.$c.agp - | awk -vm=1 '(/LG/ && NF>=4){{if (NF==4) $5=$4;print $1"\t"$2"\t"$3"\t"m"\t"$4"\t"$5}}' | gzip + awk -vn=$c '($3==n)' {input.lift} | awk -f $CONDA_PREFIX/bin/liftover.awk 11_AGP/contigs/chr.$c.agp - | awk -vm=1 '(/LG/ && NF>=4){{if (NF==4) $5=$4;print $1"\t"$2"\t"$3"\t"m"\t"$4"\t"$5}}' | gzip done > {output.mareydata} 2> {log} for c in $(seq 1 {params.chrom}) do - awk -vn=$c '($3==n)' {input.lift} | awk -f software/LepAnchor/scripts/liftover.awk 11_AGP/contigs/chr.$c.agp - | awk -vm=1 '(/LG/ && NR>=4){{if (NF>4) s=0.5; else s=1;print $1"\t"$2"\t"$3"\t"m"\t"s*($4+$5)}}' | gzip + awk -vn=$c '($3==n)' {input.lift} | awk -f $CONDA_PREFIX/bin/liftover.awk 11_AGP/contigs/chr.$c.agp - | awk -vm=1 '(/LG/ && NR>=4){{if (NF>4) s=0.5; else s=1;print $1"\t"$2"\t"$3"\t"m"\t"s*($4+$5)}}' | gzip done > {output.sexavg} 2> /dev/null """ @@ -43,7 +43,7 @@ rule mareymaps: message: "Creating Marey Maps" shell: """ - Rscript software/LepAnchor/scripts/plot_marey.R {input.data} 11_AGP/contigs - Rscript scripts/LASummary.r {input.data} true - Rscript scripts/LASummarySexAvg.r {input.sexavg} + Rscript $CONDA_PREFIX/bin/plot_marey.R {input.data} 11_AGP/contigs + LASummary.r {input.data} true + LASummarySexAvg.r {input.sexavg} """ \ No newline at end of file diff --git a/rules/LepAnchor/mask_and_chain.smk b/rules/LepAnchor/mask_and_chain.smk index d286810..eaa763c 100644 --- a/rules/LepAnchor/mask_and_chain.smk +++ b/rules/LepAnchor/mask_and_chain.smk @@ -27,13 +27,23 @@ rule repeatmask: echo "- Compressing repeat-masked genome from Red" gzip --stdout 8_RepeatMask/*.msk > {output} && rm 8_RepeatMask/*.msk """ - + +rule lastz_config: + output: + ctl = "9_Chain/all_lastz.ctl", + scoremtx = "9_Chain/scoreMatrix.q" + message: "Creating LASTZ configuration inputs" + shell: + """ + generate_lastzctl.sh > {output.ctl} + generate_qscoremtx.sh > {output.scoremtx} + """ rule chain_1: input: geno = "8_RepeatMask/repeatmasked.fa.gz", - ctrl = "software/LepAnchor/deps/all_lastz.ctl", - scoremtx = "software/LepAnchor/deps/scoreMatrix.q" + ctrl = "9_Chain/all_lastz.ctl", + scoremtx = "9_Chain/scoreMatrix.q" output: out1 = "9_Chain/repeatmaskedx.sizes", out2 = "9_Chain/repeatmasked.sizes" @@ -41,9 +51,9 @@ rule chain_1: threads: 30 shell: """ - ln -srf {input} 9_Chain/ + ln -srf {input.geno} 9_Chain/ cd 9_Chain - ../software/LepAnchor/deps/step1.HM2 repeatmasked {threads} + step1.HM2 repeatmasked {threads} """ @@ -59,6 +69,6 @@ rule chain_2: shell: """ cd 9_Chain - ../software/LepAnchor/deps/step2.HM2 repeatmasked {threads} && rm -r repeatmasked.repeatmaskedx.result/raw.axt + step2.HM2 repeatmasked {threads} && rm -r repeatmasked.repeatmaskedx.result/raw.axt ln -sr ../{output.original} ../{output.slink} """ diff --git a/rules/LepAnchor/place_orient1.smk b/rules/LepAnchor/place_orient1.smk index eb21666..4b97cca 100644 --- a/rules/LepAnchor/place_orient1.smk +++ b/rules/LepAnchor/place_orient1.smk @@ -16,7 +16,7 @@ rule place_orient: message: "Running the 1st round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} """ rule propogate1: @@ -29,5 +29,5 @@ rule propogate1: message: "First round of propogation with propogate4.awk" shell: """ - awk -f software/LepAnchor/scripts/propagate4.awk pass=1 {input.placed} pass=2 {input.errors} | awk -f software/LepAnchor/scripts/pickbed.awk - {input.bedfile} > {output.propogated} + awk -f $CONDA_PREFIX/bin/propagate4.awk pass=1 {input.placed} pass=2 {input.errors} | awk -f $CONDA_PREFIX/bin/pickbed.awk - {input.bedfile} > {output.propogated} """ diff --git a/rules/LepAnchor/place_orient2.smk b/rules/LepAnchor/place_orient2.smk index 912b9ba..b6ad01d 100644 --- a/rules/LepAnchor/place_orient2.smk +++ b/rules/LepAnchor/place_orient2.smk @@ -17,7 +17,7 @@ rule place_orient2: message: "Running 2nd round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f $CONDA_PREFIX/bin/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} """ rule propogate2: @@ -33,16 +33,16 @@ rule propogate2: message: "Second round of propogation" shell: """ - awk -f software/LepAnchor/scripts/propagate.awk {input.placed} > {output.iter1} - awk -f software/LepAnchor/scripts/propagate.awk {output.iter1} > {output.iter2} + awk -f $CONDA_PREFIX/bin/propagate.awk {input.placed} > {output.iter1} + awk -f $CONDA_PREFIX/bin/propagate.awk {output.iter1} > {output.iter2} i=2 while ! cmp -s "10_PlaceAndOrientContigs/tmp$i.la" "10_PlaceAndOrientContigs/tmp$(( $i-1 )).la" ;do - awk -f software/LepAnchor/scripts/propagate.awk 10_PlaceAndOrientContigs/tmp$i.la > 10_PlaceAndOrientContigs/tmp$[$i+1].la + awk -f $CONDA_PREFIX/bin/propagate.awk 10_PlaceAndOrientContigs/tmp$i.la > 10_PlaceAndOrientContigs/tmp$[$i+1].la i=$[$i+1] done #create prop*.la - awk -f software/LepAnchor/scripts/propagate2.awk 10_PlaceAndOrientContigs/tmp$i.la | awk '(/^[^#]/ && NF>=8){{++d[$1"\t"($7+0)"\t"($8+0)]; data[++line]=$0}}END{{for (i=1; i<=line; ++i) {{$0=data[i];if (d[$1"\t"($7+0)"\t"($8+0)] == 1) {{fn="10_PlaceAndOrientContigs/propogate/propogated."$5".la";print $0>fn}}}}}}' - awk '{{print $1"\t"($7+0)"\t"($8+0)"\t?\t"$5}}' {output.prop} | awk -f software/LepAnchor/scripts/pickbed.awk - {input.bedfile} > {output.propogated} + awk -f $CONDA_PREFIX/bin/propagate2.awk 10_PlaceAndOrientContigs/tmp$i.la | awk '(/^[^#]/ && NF>=8){{++d[$1"\t"($7+0)"\t"($8+0)]; data[++line]=$0}}END{{for (i=1; i<=line; ++i) {{$0=data[i];if (d[$1"\t"($7+0)"\t"($8+0)] == 1) {{fn="10_PlaceAndOrientContigs/propogate/propogated."$5".la";print $0>fn}}}}}}' + awk '{{print $1"\t"($7+0)"\t"($8+0)"\t?\t"$5}}' {output.prop} | awk -f $CONDA_PREFIX/bin/pickbed.awk - {input.bedfile} > {output.propogated} """ \ No newline at end of file diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index 8193be7..57f7651 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -18,7 +18,7 @@ rule place_orient3: threads: 2 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f $CONDA_PREFIX/bin/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} """ rule prune_contigblocks: @@ -29,7 +29,7 @@ rule prune_contigblocks: message: "Pruning contig blocks without map support and removing overlaps" params: chrom = lg - shell: "awk -f software/LepAnchor/scripts/prune.awk {input} > {output.chrom} 2> {output.err}" + shell: "awk -f $CONDA_PREFIX/bin/prune.awk {input} > {output.chrom} 2> {output.err}" rule prune_post: input: @@ -44,8 +44,8 @@ rule prune_post: shell: """ cat {input.prunederr} > {output.pruned} - awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} - """ + awk -f $CONDA_PREFIX/bin/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} + """ #rule find_haplotypes2: # input: @@ -72,7 +72,7 @@ rule prune_post: # threads: 1 # shell: # """ -# gunzip -fc {input.chain} | java -cp software/LepAnchor LiftoverHaplotypes map={input.chrom} haplotypes={input.haplos} chain=- > {output} +# gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ LiftoverHaplotypes map={input.chrom} haplotypes={input.haplos} chain=- > {output} # """ # #rule removehaplotypes: @@ -83,5 +83,5 @@ rule prune_post: # bedfile = "10_PlaceAndOrientContigs/map.propogated2.nohaplo.bed" # message: "Removing haplotypes from the map" # threads: 1 -# shell: "awk -f software/LepAnchor/scripts/removeHaplotypes.awk {input} > {output}" +# shell: "awk -f $CONDA_PREFIX/bin/removeHaplotypes.awk {input} > {output}" # \ No newline at end of file diff --git a/rules/LepAnchor/place_orient4.smk b/rules/LepAnchor/place_orient4.smk index f7c0f40..1640b17 100644 --- a/rules/LepAnchor/place_orient4.smk +++ b/rules/LepAnchor/place_orient4.smk @@ -18,7 +18,7 @@ rule place_orient4: threads: 2 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f $CONDA_PREFIX/bin/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} """ rule prune_contigblocks: @@ -28,7 +28,7 @@ rule prune_contigblocks: message: "Pruning contig blocks without map support and removing overlaps" params: chrom = lg - shell: "awk -f software/LepAnchor/scripts/prune.awk {input} > {output.chrom} 2> {output.err}" + shell: "awk -f $CONDA_PREFIX/bin/prune.awk {input} > {output.chrom} 2> {output.err}" rule prune_post: input: @@ -43,5 +43,5 @@ rule prune_post: shell: """ cat {input.prunederr} > {output.pruned} - awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} + awk -f $CONDA_PREFIX/bin/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} """ \ No newline at end of file diff --git a/rules/LepAnchor/trim_edges.smk b/rules/LepAnchor/trim_edges.smk index e00a10e..5c41824 100644 --- a/rules/LepAnchor/trim_edges.smk +++ b/rules/LepAnchor/trim_edges.smk @@ -19,7 +19,7 @@ rule trim_newintervals: params: edge = edgelen, dist = trimdist - shell: "Rscript scripts/LepWrapTrim.r {input} {params.dist} {params.edge} 15_Trim" + shell: "LepWrapTrim.r {input} {params.dist} {params.edge} 15_Trim" rule merge_trimplots: @@ -40,4 +40,4 @@ rule plot_trimmedintervals: input: "16_MareyMapsTrimmed/data.marey.trimmed.gz" output: report("16_MareyMapsTrimmed/LepAnchor.mareymaps.pdf", category = "Trimmed Marey Maps") message: "Plotting results of edge trimming" - shell: "Rscript scripts/LASummary.r {input}" \ No newline at end of file + shell: "LASummary.r {input}" \ No newline at end of file diff --git a/rules/LepMap3/distances.smk b/rules/LepMap3/distances.smk index ef94e55..65ce7f7 100644 --- a/rules/LepMap3/distances.smk +++ b/rules/LepMap3/distances.smk @@ -19,10 +19,10 @@ rule calculate_distances: """ cp {input.lg} {output.distance} - zcat {input.data_call} | java -cp software/LepMap3 OrderMarkers2 data=- evaluateOrder={input.lg} {params.dist_method} numThreads={threads} improveOrder=0 sexAveraged=1 &> {output.sex_averagedtmp} + zcat {input.data_call} | java -cp $CONDA_PREFIX/bin/ OrderMarkers2 evaluateOrder={input.lg} data=- {params.dist_method} numThreads={threads} improveOrder=0 sexAveraged=1 &> {output.sex_averagedtmp} sed -i -e 's/LG \= 0/LG \= {params.lg}/g' {output.sex_averagedtmp} sed -n '/\*\*\* LG \=/,$p' {output.sex_averagedtmp} > {output.sex_averaged} awk '/#java/{{flag=1}} flag; /*** LG =/{{flag=0}}' {output.sex_averagedtmp} > {log.sex_averaged} - zcat {input.data_call} | java -cp software/LepMap3 OrderMarkers2 data=- evaluateOrder={input.lg} {params.dist_method} numThreads={threads} calculateIntervals={output.intervals} > {log.intervals} 2>&1 + zcat {input.data_call} | java -cp $CONDA_PREFIX/bin/ OrderMarkers2 evaluateOrder={input.lg} data=- {params.dist_method} numThreads={threads} calculateIntervals={output.intervals} > {log.intervals} 2>&1 """ \ No newline at end of file diff --git a/rules/LepMap3/generate_map.smk b/rules/LepMap3/generate_map.smk index 5612f06..092e4bf 100644 --- a/rules/LepMap3/generate_map.smk +++ b/rules/LepMap3/generate_map.smk @@ -9,7 +9,7 @@ rule separate_chromosomes: extra = sepchrom_extra, shell: """ - zcat {input} | java -cp software/LepMap3 SeparateChromosomes2 data=- {params.extra} {informative} lodLimit={params.lod} numThreads={threads} > {output} 2> {log} + zcat {input} | java -cp $CONDA_PREFIX/bin/ SeparateChromosomes2 lodLimit={params.lod} data=- {params.extra} {informative} numThreads={threads} > {output} 2> {log} """ @@ -17,7 +17,7 @@ rule map_summary: input: expand("3_SeparateChromosomes/LOD.{LOD}", LOD = lod_range) output: "3_SeparateChromosomes/all.LOD.summary" message: "Summarizing SeperateChromosomes2 maps >> {output}" - shell: "scripts/MapSummary.r 3_SeparateChromosomes" + shell: "MapSummary.r 3_SeparateChromosomes" rule choose_map: @@ -51,7 +51,7 @@ rule join_singles: JS2A=$(echo {params.run_js2all} | tr '[:upper:]' '[:lower:]') THEMAP=$(tail -1 {input.map_choice}) if [ $JS2A == "true" ]; then - zcat {input.datacall} | java -cp software/LepMap3 JoinSingles2All map=$THEMAP data=- {params.extra} {params.lod_limit} {params.lod_diff} numThreads={threads} > {output} + zcat {input.datacall} | java -cp $CONDA_PREFIX/bin/ JoinSingles2All map=$THEMAP data=- {params.extra} {params.lod_limit} {params.lod_diff} numThreads={threads} > {output} else echo -e "\nSkipping JoinSingles2All and creating a symlink to $THEMAP instead" ln -sr $THEMAP {output} diff --git a/rules/LepMap3/order.smk b/rules/LepMap3/order.smk index 4d36f46..d0a5c52 100644 --- a/rules/LepMap3/order.smk +++ b/rules/LepMap3/order.smk @@ -15,7 +15,7 @@ rule order_markers: threads: 2 shell: """ - zcat {input.datacall} | java -cp software/LepMap3 OrderMarkers2 map={input.filt_map} {params.extra} data=- numThreads={threads} chromosome={params.chrom} &> {output.runlog} + zcat {input.datacall} | java -cp $CONDA_PREFIX/bin/ OrderMarkers2 chromosome={params.chrom} map={input.filt_map} {params.extra} data=- numThreads={threads} &> {output.runlog} sed -n '/\*\*\* LG \=/,$p' {output.runlog} > {output.lg} grep "recombin" {output.runlog} > {log.recomb} awk '/#java/{{flag=1}} flag; /logL/{{flag=0}}' {output.runlog} > {log.run} @@ -25,7 +25,4 @@ rule recomb_summary: input: expand("4_OrderMarkers/ordered.{lg}", lg = lg_range) output: "4_OrderMarkers/recombination/recombination.summary" message: "Recombination summary: {output}" - shell: - """ - Rscript scripts/RecombinationSummary.r 4_OrderMarkers/recombination > {output} - """ + shell: "RecombinationSummary.r 4_OrderMarkers/recombination > {output}" diff --git a/rules/LepMap3/prepare_data.smk b/rules/LepMap3/prepare_data.smk index fa7b5b9..bbcac5e 100644 --- a/rules/LepMap3/prepare_data.smk +++ b/rules/LepMap3/prepare_data.smk @@ -6,7 +6,7 @@ rule parent_call: message: "Creating Lep-Map3 data file from {input.vcf} and {input.pedigree}" params: extra = parentcall_extra - shell: "java -cp software/LepMap3 ParentCall2 data={input.pedigree} vcfFile={input.vcf} {params} | gzip > {output}" + shell: "java -cp $CONDA_PREFIX/bin/ ParentCall2 data={input.pedigree} vcfFile={input.vcf} {params} | gzip > {output}" rule filtering: input: "1_ParentCall/data.lepmap3.gz" @@ -21,6 +21,6 @@ rule filtering: echo "Skipping Filtering2 and creating symlink {output} instead" ln -sr {input} {output} else - zcat {input} | java -cp software/LepMap3 Filtering2 data=- dataTolerance={params.data_tolerance} {params.extra} | gzip > {output} + zcat {input} | java -cp $CONDA_PREFIX/bin/ Filtering2 data=- dataTolerance={params.data_tolerance} {params.extra} | gzip > {output} fi """ diff --git a/rules/LepMap3/reorder.smk b/rules/LepMap3/reorder.smk index 18e570a..f5654b2 100644 --- a/rules/LepMap3/reorder.smk +++ b/rules/LepMap3/reorder.smk @@ -16,7 +16,7 @@ rule reorder_markers: threads: 2 shell: """ - zcat {input.datacall} | java -cp software/LepMap3 OrderMarkers2 {params.extra} map={input.filt_map} data=- numThreads={threads} evaluateOrder={input.lg_order} &> {output.runlog} + zcat {input.datacall} | java -cp $CONDA_PREFIX/bin/ OrderMarkers2 evaluateOrder={input.lg_order} {params.extra} map={input.filt_map} data=- numThreads={threads} &> {output.runlog} sed -n '/\*\*\* LG \=/,$p' {output.runlog} > {output.lg} grep "recombin" {output.runlog} > {log.recomb} awk '/#java/{{flag=1}} flag; /logL/{{flag=0}}' {output.runlog} > {log.run} @@ -28,5 +28,5 @@ rule reorder_summary: message: "Recombination summary of reordering: {output}" shell: """ - Rscript scripts/RecombinationSummary.r 6_OrderMarkers/recombination > {output} + RecombinationSummary.r 6_OrderMarkers/recombination > {output} """ diff --git a/rules/LepMap3/trim.smk b/rules/LepMap3/trim.smk index 7f122c1..2021782 100644 --- a/rules/LepMap3/trim.smk +++ b/rules/LepMap3/trim.smk @@ -10,7 +10,7 @@ rule trim_edge_clusters: message: "Removing edge clusters >{params.trim_threshold}%cM apart from the other markers at the ends of {input}" shell: """ - Rscript scripts/LepWrapTrim.r {input} {params.trim_threshold} {params.edge_length} 5_Trim + LepWrapTrim.r {input} {params.trim_threshold} {params.edge_length} 5_Trim """ rule trim_summary: @@ -33,8 +33,8 @@ rule trim_summary: BASE=$(basename $each | cut -d "." -f1,2) sed -e "s/^/$BASE /" $each done | sort -V > {output.detailed} - scripts/TrimCounts.r {output.detailed} {params.lg} > {output.summary} - scripts/TrimSummaryPlot.r {output.summary} + TrimCounts.r {output.detailed} {params.lg} > {output.summary} + TrimSummaryPlot.r {output.summary} echo "Merging QC plots for all linkage groups" convert -density 300 {input.plots} {output.mergeplots} """ diff --git a/scripts/FilterLinkageMap.r b/scripts/FilterLinkageMap.r index 31fa7ff..c7c995b 100755 --- a/scripts/FilterLinkageMap.r +++ b/scripts/FilterLinkageMap.r @@ -1,3 +1,5 @@ +#! /usr/bin/env Rscript + # This R file performs an adaptive method of filtering a linkage map # It works by creating a spline on the linkage map, then performing # a sliding window analysis on the residuals of the spline, calculating diff --git a/scripts/RecombinationSummary.r b/scripts/RecombinationSummary.r index e792bf4..fb37bbd 100755 --- a/scripts/RecombinationSummary.r +++ b/scripts/RecombinationSummary.r @@ -1,4 +1,5 @@ #! /usr/bin/env Rscript + # This script will parse all the recombination logs of LepWrap suppressMessages(library(tidyverse)) suppressMessages(library("stringr")) diff --git a/scripts/generate_config.sh b/scripts/generate_config.sh new file mode 100755 index 0000000..232b018 --- /dev/null +++ b/scripts/generate_config.sh @@ -0,0 +1,151 @@ +#! /usr/bin/env bash + +cat <], # inferred score output to this file. + +##dynamic masking +# equivalent to the BLASTZ_M, default is 0, when required, set to 50 normally, some set to 254 +--masking=254 +# --notrivial is required for haploMerger, should not changed! +--notrivial + +##scoring parameters for tuning +#--scores= # should not specify when using scoreMatrix inference procedure, i.e. --infer +#--inner=2000 # equivalent to the BLASTZ_H, normally H=2000 +--hspthresh=3000 # equivalent to the BLASTZ_K, 3000 by default +--ydrop=3400 # equivalent to the BLASTZ_Y, O+300E by default +--gappedthresh=3000 # equivalent to the BLASTZ_L, L=K by default + +################################################## +##scoring parameters usually don't need change + +# equivalent to BLASTZ_O and BLASTZ_E, 400 and 30, by default +--gap=400,30 +#--nogapped + +#Offset between the starting positions of successive target words considered for potential seeds. +#(But this does not apply to the query words, which always use a step size of 1.) +# equivalent to the BLASTZ_Z, z=1 by default +# set to 20 can reduce the memory consumtion by a factor of 3 +--step=20 + +# by default --seed=12of19 , or --seed=14of22 +--seed=12of19 + +# default setting=--transition, alternative --notransition +# set to --notranstion can shorten the time by a factor of 10 +--notransition + +################################################## +##non scoring parameters usually don't need change +--format=axt # lav (default), axt, maf, and so on +--markend # Just before normal completion, write "# lastz end-of-file" to the output file. +--verbosity=1 # logfile details +EOF \ No newline at end of file diff --git a/scripts/generate_qscoremtx.sh b/scripts/generate_qscoremtx.sh new file mode 100755 index 0000000..7385953 --- /dev/null +++ b/scripts/generate_qscoremtx.sh @@ -0,0 +1,9 @@ +#! /usr/bin/env bash + +cat < JoinSingles2All_iter/logs/map.$i.$4.js2all + zcat 2_Filtering/data.filtered.lepmap3.gz | java -cp $CONDA_PREFIX/bin/ JoinSingles2All map=$TARGETMAP data=- lodLimit=$i lodDifference=4 iterate=1 distortionLod=1 numThreads=10 informativeMask=$INFMASK > JoinSingles2All_iter/logs/map.$i.$4.js2all cut -f1 JoinSingles2All_iter/logs/map.$i.$4.js2all > JoinSingles2All_iter/LOD.$i.$4.js2all done diff --git a/scripts/refinemap.sh b/scripts/refinemap.sh index 092f0b2..fd60344 100755 --- a/scripts/refinemap.sh +++ b/scripts/refinemap.sh @@ -31,7 +31,7 @@ LODMAX=$4 SIZELIM=$5 for i in $(seq $LODMIN $LODMAX); do - zcat 2_Filtering/data.filtered.lepmap3.gz | java -cp software/LepMap3 SeparateChromosomes2 data=- map=$TARGETMAP lg=$TARGETLG sizeLimit=$SIZELIM lodLimit=$i distortionLod=1 numThreads=4 > $1.refine/map.$i + zcat 2_Filtering/data.filtered.lepmap3.gz | java -cp $CONDA_PREFIX/bin/ SeparateChromosomes2 data=- map=$TARGETMAP lg=$TARGETLG sizeLimit=$SIZELIM lodLimit=$i distortionLod=1 numThreads=4 > $1.refine/map.$i done # generate a summary of the results diff --git a/scripts/usage b/scripts/usage index ab95740..d7a1411 100755 --- a/scripts/usage +++ b/scripts/usage @@ -27,7 +27,7 @@ EOF fi if [[ $1 == "Map2Bed" || $1 == "CleanMap" || $1 == "PlaceAndOrientContigs" ]]; then - java -cp software/LepAnchor $1 2>&1 + java -cp $CONDA_PREFIX/bin/ $1 2>&1 else - java -cp software/LepMap3 $1 2>&1 | tail -n +2 + java -cp $CONDA_PREFIX/bin/ $1 2>&1 | tail -n +2 fi diff --git a/thingsx.seq b/thingsx.seq new file mode 120000 index 0000000..9fedc8c --- /dev/null +++ b/thingsx.seq @@ -0,0 +1 @@ +things.seq \ No newline at end of file diff --git a/thingsx.sizes b/thingsx.sizes new file mode 120000 index 0000000..df0fce3 --- /dev/null +++ b/thingsx.sizes @@ -0,0 +1 @@ +things.sizes \ No newline at end of file diff --git a/x.seq b/x.seq new file mode 120000 index 0000000..0fc2021 --- /dev/null +++ b/x.seq @@ -0,0 +1 @@ +.seq \ No newline at end of file diff --git a/x.sizes b/x.sizes new file mode 120000 index 0000000..4e9432d --- /dev/null +++ b/x.sizes @@ -0,0 +1 @@ +.sizes \ No newline at end of file From 0f91a76fb8984eb1cf3488e2faef1b6830f2a2cd Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Mon, 14 Feb 2022 12:49:10 -0500 Subject: [PATCH 35/46] update paths and error text --- .misc/install.sh | 20 -- HM.all_lastz.log | 0 HM.axtChainRecipBestNet.log | 1 - HM.initiation.log | 1 - HM2.all_lastz.log | 3 - LepWrap | 24 +- conda_setup.yml | 363 ------------------------------ rules/LepAnchor/place_orient3.smk | 87 ------- software/LepAnchor/deps/step1.HM2 | 4 +- software/LepAnchor/deps/step2.HM2 | 2 +- thingsx.seq | 1 - thingsx.sizes | 1 - x.seq | 1 - x.sizes | 1 - 14 files changed, 21 insertions(+), 488 deletions(-) delete mode 100644 .misc/install.sh delete mode 100644 HM.all_lastz.log delete mode 100644 HM.axtChainRecipBestNet.log delete mode 100644 HM.initiation.log delete mode 100644 HM2.all_lastz.log delete mode 100644 conda_setup.yml delete mode 100644 rules/LepAnchor/place_orient3.smk delete mode 120000 thingsx.seq delete mode 120000 thingsx.sizes delete mode 120000 x.seq delete mode 120000 x.sizes diff --git a/.misc/install.sh b/.misc/install.sh deleted file mode 100644 index d87f015..0000000 --- a/.misc/install.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /usr/bin/env bash - -# install LepWrap into conda PATH -mkdir -p $CONDA_PREFIX/bin -# LepWrap executable -cp LepWrap $CONDA_PREFIX/bin/ -chmod +x $CONDA_PREFIX/bin/LepWrap -# associated scripts -chmod +x scripts/* -cp scripts/* $CONDA_PREFIX/bin/ -# LepMap3 modules and scripts -cp software/LepMap3/*.class $CONDA_PREFIX/bin -cp software/LepMap3/scripts/* $CONDA_PREFIX/bin -# LepAnchor modules and scripts -cp software/LepAnchor/*.class software/LepAnchor/lepanchor_wrapper.sh $CONDA_PREFIX/bin -cp $CONDA_PREFIX/bin/* $CONDA_PREFIX/bin -cp software/LepAnchor/deps/ucsc_binaries/* $CONDA_PREFIX/bin -cp software/LepAnchor/deps/*.pl software/LepAnchor/deps/Red software/LepAnchor/deps/all_lastz.ctl software/LepAnchor/deps/scoreMatrix.q software/LepAnchor/deps/step* $CONDA_PREFIX/bin -# Snakemake rules -cp rules/LepAnchor/*.smk rules/LepMap3/*.smk $CONDA_PREFIX/bin diff --git a/HM.all_lastz.log b/HM.all_lastz.log deleted file mode 100644 index e69de29..0000000 diff --git a/HM.axtChainRecipBestNet.log b/HM.axtChainRecipBestNet.log deleted file mode 100644 index 9efda7c..0000000 --- a/HM.axtChainRecipBestNet.log +++ /dev/null @@ -1 +0,0 @@ -Can't open perl script "../software/LepAnchor/deps/HM_axtChainRecipBestNet.pl": No such file or directory diff --git a/HM.initiation.log b/HM.initiation.log deleted file mode 100644 index 1134612..0000000 --- a/HM.initiation.log +++ /dev/null @@ -1 +0,0 @@ -Can't open perl script "../software/LepAnchor/deps/initiation.pl": No such file or directory diff --git a/HM2.all_lastz.log b/HM2.all_lastz.log deleted file mode 100644 index 7b3c223..0000000 --- a/HM2.all_lastz.log +++ /dev/null @@ -1,3 +0,0 @@ -Can't open perl script "../software/LepAnchor/deps/HM_all_lastz_mThreads.pl": No such file or directory -Can't open perl script "../software/LepAnchor/deps/HM_all_lastz_mThreads.pl": No such file or directory -Can't open perl script "../software/LepAnchor/deps/HM_all_lastz_mThreads.pl": No such file or directory diff --git a/LepWrap b/LepWrap index 2b78c49..2921105 100755 --- a/LepWrap +++ b/LepWrap @@ -20,12 +20,29 @@ if [[ -z "$1" ]]; then exit 1 fi +if [ -z "$CONDA_PREFIX" ]; then + echo -e "No active conda environment detected and one is required for the pipeline to work. If you cloned the repository rather than installing the conda package, you may install the environment using:\n" + printf "\033[01;32m" + printf "conda env create -f conda_setup.yml\n\n" + printf "\033[0m" + echo -e "Then, activate it using:\n" + printf "\033[01;32m" + printf "conda activate lepwrap\n" + printf "\033[0m" + exit 1 +fi + # Check for snakemake if which snakemake &>/dev/null; then foo=1 else echo -e "ERROR:\nSnakemake installation is required to run LepWrap, but not found in the current environment." - echo -e "If [ana|mini]conda are installed, created a pre-configred environment with:\n" + echo -e "It is likely LepWrap was not installed using [ana|mini]conda, which bundles all the dependencies." + echo -e "Please install LepWrap using conda: " + printf "\033[01;32m" + printf "conda install -c bioconda lepwrap\n\n" + printf "\033[0m" + echo -e "Alternatively, if you cloned the repository, you may install the environment using:\n" printf "\033[01;32m" printf "conda env create -f conda_setup.yml\n" printf "\033[0m" @@ -85,11 +102,6 @@ lepanchor(){ snakemake --cores $1 --snakefile $CONDA_PREFIX/bin/LepAnchor.smk --configfile $CONF --directory . } -if [ -z "$CONDA_PREFIX" ]; then - echo "No active conda environment detected. The LepWrap conda environment is required for the pipeline to work." - exit 1 -fi - LM=$(grep "run_lepmap" config.yml | cut -d":" -f2 | xargs | tr '[:upper:]' '[:lower:]') LA=$(grep "run_lepanchor" config.yml | cut -d":" -f2 | xargs | tr '[:upper:]' '[:lower:]') diff --git a/conda_setup.yml b/conda_setup.yml deleted file mode 100644 index c81b970..0000000 --- a/conda_setup.yml +++ /dev/null @@ -1,363 +0,0 @@ -name: lepwrap -channels: - - conda-forge/label/main - - bioconda - - conda-forge - - defaults - - r -dependencies: - - _libgcc_mutex=0.1=conda_forge - - _openmp_mutex=4.5=1_gnu - - _r-mutex=1.0.1=anacondar_1 - - aioeasywebdav=2.4.0=py39hf3d152e_1001 - - aiohttp=3.7.4=py39h3811e60_0 - - amply=0.1.4=py_0 - - appdirs=1.4.4=pyh9f0ad1d_0 - - async-timeout=3.0.1=py_1000 - - atk-1.0=2.36.0=h3371d22_4 - - attmap=0.13.0=pyhd8ed1ab_0 - - attrs=21.2.0=pyhd8ed1ab_0 - - azure-common=1.1.27=pyhd8ed1ab_0 - - azure-core=1.14.0=pyhd8ed1ab_0 - - azure-storage-blob=12.8.1=pyhd8ed1ab_0 - - backports=1.0=py_2 - - backports.functools_lru_cache=1.6.4=pyhd8ed1ab_0 - - bcrypt=3.2.0=py39h3811e60_1 - - binutils_impl_linux-64=2.35.1=h193b22a_2 - - binutils_linux-64=2.35=h67ddf6f_30 - - blinker=1.4=py_1 - - boto=2.49.0=py_0 - - boto3=1.17.76=pyhd8ed1ab_0 - - botocore=1.20.76=pyhd8ed1ab_0 - - brotlipy=0.7.0=py39h3811e60_1001 - - bwidget=1.9.14=ha770c72_0 - - bzip2=1.0.8=h7f98852_4 - - c-ares=1.17.1=h7f98852_1 - - ca-certificates=2020.12.5=ha878542_0 - - cachetools=4.2.2=pyhd8ed1ab_0 - - cairo=1.16.0=h6cf1ce9_1008 - - certifi=2020.12.5=py39hf3d152e_1 - - cffi=1.14.5=py39he32792d_0 - - chardet=4.0.0=py39hf3d152e_1 - - coincbc=2.10.5=hcee13e7_1 - - configargparse=1.4.1=pyhd8ed1ab_0 - - connection_pool=0.0.3=pyhd3deb0d_0 - - cryptography=3.4.7=py39hbca0aa6_0 - - curl=7.76.1=hea6ffbf_2 - - datrie=0.8.2=py39h3811e60_2 - - decorator=5.0.9=pyhd8ed1ab_0 - - docutils=0.17.1=py39hf3d152e_0 - - dropbox=11.9.0=pyhd8ed1ab_0 - - expat=2.3.0=h9c3ff4c_0 - - fftw=3.3.9=nompi_h74d3f13_101 - - filechunkio=1.8=py_2 - - filelock=3.0.12=pyh9f0ad1d_0 - - font-ttf-dejavu-sans-mono=2.37=hab24e00_0 - - font-ttf-inconsolata=3.000=h77eed37_0 - - font-ttf-source-code-pro=2.038=h77eed37_0 - - font-ttf-ubuntu=0.83=hab24e00_0 - - fontconfig=2.13.1=hba837de_1005 - - fonts-conda-ecosystem=1=0 - - fonts-conda-forge=1=0 - - freetype=2.10.4=h0708190_1 - - fribidi=1.0.10=h36c2ea0_0 - - ftputil=5.0.1=pyhd8ed1ab_0 - - gcc_impl_linux-64=9.3.0=h70c0ae5_19 - - gcc_linux-64=9.3.0=hf25ea35_30 - - gdk-pixbuf=2.42.6=h04a7f16_0 - - gettext=0.19.8.1=h0b5b191_1005 - - gfortran_impl_linux-64=9.3.0=hc4a2995_19 - - gfortran_linux-64=9.3.0=hdc58fab_30 - - ghostscript=9.18=1 - - giflib=5.2.1=h36c2ea0_2 - - gitdb=4.0.7=pyhd8ed1ab_0 - - gitpython=3.1.17=pyhd8ed1ab_0 - - google-api-core=1.26.3=pyhd8ed1ab_0 - - google-api-python-client=2.5.0=pyhd8ed1ab_0 - - google-auth=1.30.0=pyh44b312d_0 - - google-auth-httplib2=0.1.0=pyhd8ed1ab_0 - - google-cloud-core=1.5.0=pyhd3deb0d_0 - - google-cloud-storage=1.19.0=py_0 - - google-crc32c=1.1.2=py39hb81f231_0 - - google-resumable-media=1.2.0=pyhd3deb0d_0 - - googleapis-common-protos=1.53.0=py39hf3d152e_0 - - graphite2=1.3.13=h58526e2_1001 - - graphviz=2.47.1=h85b4f2f_1 - - grpcio=1.37.1=py39hff7568b_0 - - gsl=2.6=he838d99_2 - - gtk2=2.24.33=h539f30e_1 - - gts=0.7.6=h64030ff_2 - - gxx_impl_linux-64=9.3.0=hd87eabc_19 - - gxx_linux-64=9.3.0=h3fbe746_30 - - harfbuzz=2.8.1=h83ec7ef_0 - - httplib2=0.19.1=pyhd8ed1ab_0 - - icu=68.1=h58526e2_0 - - idna=2.10=pyh9f0ad1d_0 - - imagemagick=7.0.11_13=pl5320hb118871_0 - - importlib-metadata=4.0.1=py39hf3d152e_0 - - iniconfig=1.1.1=pyh9f0ad1d_0 - - ipython_genutils=0.2.0=py_1 - - isodate=0.6.0=py_1 - - jbig=2.1=h7f98852_2003 - - jinja2=3.0.1=pyhd8ed1ab_0 - - jmespath=0.10.0=pyh9f0ad1d_0 - - jpeg=9d=h36c2ea0_0 - - jsonschema=3.2.0=pyhd8ed1ab_3 - - jupyter_core=4.7.1=py39hf3d152e_0 - - kernel-headers_linux-64=2.6.32=h77966d4_13 - - krb5=1.19.1=hcc1bbae_0 - - ld_impl_linux-64=2.35.1=hea4e1c9_2 - - libblas=3.9.0=9_openblas - - libcblas=3.9.0=9_openblas - - libcrc32c=1.1.1=h9c3ff4c_2 - - libcurl=7.76.1=h2574ce0_2 - - libedit=3.1.20191231=he28a2e2_2 - - libev=4.33=h516909a_1 - - libffi=3.3=h58526e2_2 - - libgcc=7.2.0=h69d50b8_2 - - libgcc-devel_linux-64=9.3.0=h7864c58_19 - - libgcc-ng=9.3.0=h2828fa1_19 - - libgd=2.3.2=h78a0170_0 - - libgfortran-ng=9.3.0=hff62375_19 - - libgfortran5=9.3.0=hff62375_19 - - libglib=2.68.2=h3e27bee_0 - - libgomp=9.3.0=h2828fa1_19 - - libiconv=1.16=h516909a_0 - - liblapack=3.9.0=9_openblas - - libnghttp2=1.43.0=h812cca2_0 - - libopenblas=0.3.15=pthreads_h8fe5266_1 - - libpng=1.6.37=h21135ba_2 - - libprotobuf=3.17.0=h780b84a_0 - - librsvg=2.50.5=hc3c00ef_0 - - libsodium=1.0.18=h36c2ea0_1 - - libssh2=1.9.0=ha56f1ee_6 - - libstdcxx-devel_linux-64=9.3.0=hb016644_19 - - libstdcxx-ng=9.3.0=h6de172a_19 - - libtiff=4.3.0=hf544144_0 - - libtool=2.4.6=h58526e2_1007 - - libuuid=2.32.1=h7f98852_1000 - - libwebp=1.2.0=h3452ae3_0 - - libwebp-base=1.2.0=h7f98852_2 - - libxcb=1.13=h7f98852_1003 - - libxml2=2.9.12=h72842e0_0 - - logmuse=0.2.6=pyh8c360ce_0 - - lz4-c=1.9.3=h9c3ff4c_0 - - make=4.3=hd18ef5c_1 - - markupsafe=2.0.1=py39h3811e60_0 - - more-itertools=8.7.0=pyhd8ed1ab_1 - - msrest=0.6.21=pyh44b312d_0 - - multidict=5.1.0=py39h3811e60_1 - - nbformat=5.1.3=pyhd8ed1ab_0 - - ncurses=6.2=h58526e2_4 - - networkx=2.5=py_0 - - numpy=1.20.3=py39hdbf815f_0 - - oauth2client=4.1.3=py_0 - - oauthlib=3.0.1=py_0 - - openjpeg=2.4.0=hb52868f_1 - - openssl=1.1.1k=h7f98852_0 - - packaging=20.9=pyh44b312d_0 - - pandas=1.2.4=py39hde0f152_0 - - pandoc=2.13=h7f98852_0 - - pango=1.48.5=hb8ff022_0 - - paramiko=2.7.2=pyh9f0ad1d_0 - - pcre=8.44=he1b5a44_0 - - pcre2=10.36=h032f7d1_1 - - peppy=0.31.1=pyhd8ed1ab_0 - - perl=5.32.0=h36c2ea0_0 - - pip=21.1.1=pyhd8ed1ab_0 - - pixman=0.40.0=h36c2ea0_0 - - pkg-config=0.29.2=h36c2ea0_1008 - - pluggy=0.13.1=py39hf3d152e_4 - - ply=3.11=py_1 - - prettytable=2.1.0=pyhd8ed1ab_0 - - protobuf=3.17.0=py39he80948d_0 - - psutil=5.8.0=py39h3811e60_1 - - pthread-stubs=0.4=h36c2ea0_1001 - - pulp=2.4=py39hf3d152e_0 - - py=1.10.0=pyhd3deb0d_0 - - pyasn1=0.4.8=py_0 - - pyasn1-modules=0.2.7=py_0 - - pycparser=2.20=pyh9f0ad1d_2 - - pygments=2.9.0=pyhd8ed1ab_0 - - pygraphviz=1.7=py39h78163bd_0 - - pyjwt=2.1.0=pyhd8ed1ab_0 - - pynacl=1.4.0=py39h3811e60_2 - - pyopenssl=20.0.1=pyhd8ed1ab_0 - - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.17.3=py39h3811e60_2 - - pysftp=0.2.9=py_1 - - pysocks=1.7.1=py39hf3d152e_3 - - pytest=6.2.4=py39hf3d152e_0 - - python=3.9.4=hffdb5ce_0_cpython - - python-dateutil=2.8.1=py_0 - - python-irodsclient=0.9.0=pyhd8ed1ab_0 - - python_abi=3.9=1_cp39 - - pytz=2021.1=pyhd8ed1ab_0 - - pyyaml=5.4.1=py39h3811e60_0 - - r-askpass=1.1=r40hcdcec82_2 - - r-assertthat=0.2.1=r40h6115d3f_2 - - r-backports=1.2.1=r40hcfec24a_0 - - r-base=4.0.5=h9e01966_1 - - r-base64enc=0.1_3=r40hcdcec82_1004 - - r-blob=1.2.1=r40h6115d3f_1 - - r-brio=1.1.2=r40hcfec24a_0 - - r-broom=0.7.6=r40hc72bb7e_0 - - r-callr=3.7.0=r40hc72bb7e_0 - - r-cellranger=1.1.0=r40h6115d3f_1003 - - r-cli=2.5.0=r40hc72bb7e_0 - - r-clipr=0.7.1=r40h142f84f_0 - - r-colorspace=2.0_1=r40hcfec24a_0 - - r-cowplot=1.1.1=r40hc72bb7e_0 - - r-cpp11=0.2.7=r40hc72bb7e_0 - - r-crayon=1.4.1=r40hc72bb7e_0 - - r-curl=4.3.1=r40hcfec24a_0 - - r-data.table=1.14.0=r40hcfec24a_0 - - r-dbi=1.1.1=r40hc72bb7e_0 - - r-dbplyr=2.1.1=r40hc72bb7e_0 - - r-desc=1.3.0=r40hc72bb7e_0 - - r-diffobj=0.3.4=r40hcfec24a_0 - - r-digest=0.6.27=r40h03ef668_0 - - r-dplyr=1.0.6=r40h03ef668_1 - - r-dtplyr=1.1.0=r40hc72bb7e_0 - - r-ellipsis=0.3.2=r40hcfec24a_0 - - r-evaluate=0.14=r40h6115d3f_2 - - r-fansi=0.4.2=r40hcfec24a_0 - - r-farver=2.1.0=r40h03ef668_0 - - r-forcats=0.5.1=r40hc72bb7e_0 - - r-fs=1.5.0=r40h0357c0b_0 - - r-fuzzyjoin=0.1.6=r40_0 - - r-gargle=1.1.0=r40hc72bb7e_0 - - r-gdtools=0.2.2=r40h36050f4_1 - - r-generics=0.1.0=r40hc72bb7e_0 - - r-geosphere=1.5_10=r40hcfec24a_2 - - r-ggplot2=3.3.3=r40hc72bb7e_0 - - r-glue=1.4.2=r40hcfec24a_0 - - r-googledrive=1.0.1=r40h6115d3f_1 - - r-googlesheets4=0.3.0=r40hc72bb7e_0 - - r-gtable=0.3.0=r40h6115d3f_3 - - r-haven=2.4.1=r40h2713e49_0 - - r-highr=0.9=r40hc72bb7e_0 - - r-hms=1.1.0=r40hc72bb7e_0 - - r-htmltools=0.5.1.1=r40h03ef668_0 - - r-httr=1.4.2=r40h6115d3f_0 - - r-ids=1.0.1=r40h6115d3f_1 - - r-isoband=0.2.4=r40h03ef668_0 - - r-jsonlite=1.7.2=r40hcfec24a_0 - - r-knitr=1.33=r40hc72bb7e_0 - - r-labeling=0.4.2=r40h142f84f_0 - - r-lattice=0.20_44=r40hcfec24a_0 - - r-lifecycle=1.0.0=r40hc72bb7e_0 - - r-lubridate=1.7.10=r40h03ef668_0 - - r-magrittr=2.0.1=r40hcfec24a_1 - - r-markdown=1.1=r40hcfec24a_1 - - r-mass=7.3_54=r40hcfec24a_0 - - r-matrix=1.3_3=r40he454529_0 - - r-mgcv=1.8_35=r40he454529_0 - - r-mime=0.10=r40hcfec24a_0 - - r-modelr=0.1.8=r40h6115d3f_0 - - r-munsell=0.5.0=r40h6115d3f_1003 - - r-nlme=3.1_152=r40h859d828_0 - - r-openssl=1.4.4=r40he36bf35_0 - - r-pillar=1.6.1=r40hc72bb7e_0 - - r-pkgconfig=2.0.3=r40h6115d3f_1 - - r-pkgload=1.2.1=r40h03ef668_0 - - r-plyr=1.8.6=r40h0357c0b_1 - - r-praise=1.0.0=r40h6115d3f_1004 - - r-prettyunits=1.1.1=r40h6115d3f_1 - - r-processx=3.5.2=r40hcfec24a_0 - - r-progress=1.2.2=r40h6115d3f_2 - - r-ps=1.6.0=r40hcfec24a_0 - - r-purrr=0.3.4=r40hcfec24a_1 - - r-r6=2.5.0=r40hc72bb7e_0 - - r-rappdirs=0.3.3=r40hcfec24a_0 - - r-rcolorbrewer=1.1_2=r40h6115d3f_1003 - - r-rcpp=1.0.6=r40h03ef668_0 - - r-readr=1.4.0=r40h1b71b39_0 - - r-readxl=1.3.1=r40hde08347_4 - - r-rematch=1.0.1=r40h6115d3f_1003 - - r-rematch2=2.1.2=r40h6115d3f_1 - - r-reprex=2.0.0=r40hc72bb7e_0 - - r-reshape2=1.4.4=r40h0357c0b_1 - - r-rlang=0.4.11=r40hcfec24a_0 - - r-rmarkdown=2.8=r40hc72bb7e_0 - - r-rprojroot=2.0.2=r40hc72bb7e_0 - - r-rstudioapi=0.13=r40hc72bb7e_0 - - r-rvest=1.0.0=r40hc72bb7e_0 - - r-scales=1.1.1=r40h6115d3f_0 - - r-selectr=0.4_2=r40h6115d3f_1 - - r-sp=1.4_5=r40hcfec24a_0 - - r-stringdist=0.9.6.3=r40hcfec24a_0 - - r-stringi=1.6.2=r40hcabe038_0 - - r-stringr=1.4.0=r40h6115d3f_2 - - r-svglite=2.0.0=r40h03ef668_0 - - r-sys=3.4=r40hcdcec82_0 - - r-systemfonts=1.0.2=r40hef9c87a_0 - - r-testthat=3.0.2=r40h03ef668_0 - - r-tibble=3.1.2=r40hcfec24a_0 - - r-tidyr=1.1.3=r40h03ef668_0 - - r-tidyselect=1.1.1=r40hc72bb7e_0 - - r-tidyverse=1.3.1=r40hc72bb7e_0 - - r-tinytex=0.31=r40hc72bb7e_0 - - r-utf8=1.2.1=r40hcfec24a_0 - - r-uuid=0.1_4=r40hcdcec82_1 - - r-vctrs=0.3.8=r40hcfec24a_0 - - r-viridislite=0.4.0=r40hc72bb7e_0 - - r-waldo=0.2.5=r40hc72bb7e_0 - - r-withr=2.4.2=r40hc72bb7e_0 - - r-xfun=0.23=r40hcfec24a_0 - - r-xml2=1.3.2=r40h0357c0b_1 - - r-yaml=2.2.1=r40hcfec24a_1 - - r-zeallot=0.1.0=r40h6115d3f_1002 - - ratelimiter=1.2.0=py_1002 - - readline=8.1=h46c0cb4_0 - - requests=2.25.1=pyhd3deb0d_0 - - requests-oauthlib=1.3.0=pyh9f0ad1d_0 - - rsa=4.7.2=pyh44b312d_0 - - s3transfer=0.4.2=pyhd8ed1ab_0 - - sed=4.8=he412f7d_0 - - setuptools=49.6.0=py39hf3d152e_3 - - simplejson=3.17.2=py39h3811e60_2 - - six=1.16.0=pyh6c4a22f_0 - - slacker=0.14.0=py_0 - - smart_open=5.0.0=pyhd8ed1ab_0 - - smmap=3.0.5=pyh44b312d_0 - - snakemake=6.4.0=hdfd78af_0 - - snakemake-minimal=6.4.0=pyhdfd78af_0 - - sqlite=3.35.5=h74cdb3f_0 - - stone=3.2.1=pyhd8ed1ab_0 - - stopit=1.1.2=py_0 - - sysroot_linux-64=2.12=h77966d4_13 - - tk=8.6.10=h21135ba_1 - - tktable=2.10=hb7b940f_3 - - toml=0.10.2=pyhd8ed1ab_0 - - toposort=1.6=pyhd8ed1ab_0 - - traitlets=5.0.5=py_0 - - typing-extensions=3.7.4.3=0 - - typing_extensions=3.7.4.3=py_0 - - tzdata=2021a=he74cb21_0 - - ubiquerg=0.6.1=pyh9f0ad1d_0 - - uritemplate=3.0.1=py_0 - - urllib3=1.26.4=pyhd8ed1ab_0 - - veracitools=0.1.3=py_0 - - wcwidth=0.2.5=pyh9f0ad1d_2 - - wheel=0.36.2=pyhd3deb0d_0 - - wrapt=1.12.1=py39h3811e60_3 - - xmlrunner=1.7.7=py_0 - - xorg-kbproto=1.0.7=h7f98852_1002 - - xorg-libice=1.0.10=h7f98852_0 - - xorg-libsm=1.2.3=hd9c2040_1000 - - xorg-libx11=1.7.1=h7f98852_0 - - xorg-libxau=1.0.9=h7f98852_0 - - xorg-libxdmcp=1.1.3=h7f98852_0 - - xorg-libxext=1.3.4=h7f98852_1 - - xorg-libxrender=0.9.10=h7f98852_1003 - - xorg-libxt=1.2.1=h7f98852_2 - - xorg-renderproto=0.11.1=h7f98852_1002 - - xorg-xextproto=7.3.0=h7f98852_1002 - - xorg-xproto=7.0.31=h7f98852_1007 - - xz=5.2.5=h516909a_1 - - yaml=0.2.5=h516909a_0 - - yarl=1.6.3=py39h3811e60_1 - - zipp=3.4.1=pyhd8ed1ab_0 - - zlib=1.2.11=h516909a_1010 - - zstd=1.5.0=ha95c52a_0 diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk deleted file mode 100644 index 57f7651..0000000 --- a/rules/LepAnchor/place_orient3.smk +++ /dev/null @@ -1,87 +0,0 @@ -rule place_orient3: - input: - chain = "9_Chain/chainfile.gz", - bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", - paf = paf, - prox = proximity, - lift = "10_PlaceAndOrientContigs/liftover.la", - chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la", - propogated = "10_PlaceAndOrientContigs/propogate/propogated.{lg_range}.la" - output: - chrom = "10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la", - errors = "10_PlaceAndOrientContigs/3_orient/errors/chr.{lg_range}.err" - params: - chrom = "{lg_range}", - extras = place_orient_extra, - datatype = data_type - message: "Running 3rd round of PlaceAndOrientContigs for linkage group {params.chrom}" - threads: 2 - shell: - """ - gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f $CONDA_PREFIX/bin/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} - """ - -rule prune_contigblocks: - input: "10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la" - output: - chrom = "10_PlaceAndOrientContigs/pruned/chr.{lg_range}.pruned.la", - err = "10_PlaceAndOrientContigs/pruned/err/chr.{lg_range}.pruned.err" - message: "Pruning contig blocks without map support and removing overlaps" - params: - chrom = lg - shell: "awk -f $CONDA_PREFIX/bin/prune.awk {input} > {output.chrom} 2> {output.err}" - -rule prune_post: - input: - bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", - prunedchrom = expand("10_PlaceAndOrientContigs/pruned/chr.{lgs}.pruned.la", lgs = lg_range), - prunederr = expand("10_PlaceAndOrientContigs/pruned/err/chr.{lgs}.pruned.err", lgs = lg_range) - output: - overlaps = "10_PlaceAndOrientContigs/overlaps.removed.la", - pruned = "10_PlaceAndOrientContigs/pruned.la" - message: "Removing overlaps" - threads: 1 - shell: - """ - cat {input.prunederr} > {output.pruned} - awk -f $CONDA_PREFIX/bin/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} - """ - -#rule find_haplotypes2: -# input: -# errors = expand("10_PlaceAndOrientContigs/3_orient/errors/chr.{lgs}.err", lgs = lg_range) -# output: -# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" -# message: "Finding full haplotypes" -# params: -# haplo = haplo_limit -# shell: -# """ -# awk '($NF=="haplotype")' {input.errors} | -# sort -n -r | -# awk -vlimit={params.haplo} '($NF=="haplotype" && ($1>=($4-$3+1-limit)/limit) && (!(($5 SUBSEP $6 SUBSEP $7) in h))){{h[$2,$3,$4]; print}}' > {output.haplos} -# """ -# -#rule liftoverHaplotypes: -# input: -# chain = "9_Chain/chainfile.gz", -# chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la", -# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" -# output: "10_PlaceAndOrientContigs/liftover/chr.{lg_range}.liftover" -# message: "Running liftoverHaplotypes for {input.chrom}" -# threads: 1 -# shell: -# """ -# gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ LiftoverHaplotypes map={input.chrom} haplotypes={input.haplos} chain=- > {output} -# """ -# -#rule removehaplotypes: -# input: -# mapfile = "10_PlaceAndOrientContigs/map.propogated2.bed", -# haplos = "10_PlaceAndOrientContigs/suspected.haplotypes" -# output: -# bedfile = "10_PlaceAndOrientContigs/map.propogated2.nohaplo.bed" -# message: "Removing haplotypes from the map" -# threads: 1 -# shell: "awk -f $CONDA_PREFIX/bin/removeHaplotypes.awk {input} > {output}" -# \ No newline at end of file diff --git a/software/LepAnchor/deps/step1.HM2 b/software/LepAnchor/deps/step1.HM2 index 0849458..29fd6c1 100755 --- a/software/LepAnchor/deps/step1.HM2 +++ b/software/LepAnchor/deps/step1.HM2 @@ -38,7 +38,7 @@ querySize=1600000000 # split query fasta file by size N bp (default=1600000000) #### =========================================================== rm -f -r $name.seq ${name}x.seq $name.sizes ${name}x.sizes ${name}x.fa.gz -perl ../software/LepAnchor/deps/initiation.pl \ +perl $CONDA_PREFIX/bin/initiation.pl \ --faSplit \ --faToNib \ --faSize \ @@ -58,7 +58,7 @@ ln -s $name.seq ${name}x.seq rm -f -r $name.${name}x.result/raw.axt -perl ../software/LepAnchor/deps/HM_all_lastz_mThreads.pl \ +perl $CONDA_PREFIX/bin/HM_all_lastz_mThreads.pl \ --Species $name ${name}x \ --noself \ --threads=$threads \ diff --git a/software/LepAnchor/deps/step2.HM2 b/software/LepAnchor/deps/step2.HM2 index 1598e4b..bd764c8 100755 --- a/software/LepAnchor/deps/step2.HM2 +++ b/software/LepAnchor/deps/step2.HM2 @@ -33,7 +33,7 @@ threads=$2 # the number of cpu cores to use (default=1) #### run axtChainRecipBestNet #### =========================================================== -perl ../software/LepAnchor/deps/HM_axtChainRecipBestNet.pl \ +perl $CONDA_PREFIX/bin/HM_axtChainRecipBestNet.pl \ --rbestNet \ --axtChain \ --tbest \ diff --git a/thingsx.seq b/thingsx.seq deleted file mode 120000 index 9fedc8c..0000000 --- a/thingsx.seq +++ /dev/null @@ -1 +0,0 @@ -things.seq \ No newline at end of file diff --git a/thingsx.sizes b/thingsx.sizes deleted file mode 120000 index df0fce3..0000000 --- a/thingsx.sizes +++ /dev/null @@ -1 +0,0 @@ -things.sizes \ No newline at end of file diff --git a/x.seq b/x.seq deleted file mode 120000 index 0fc2021..0000000 --- a/x.seq +++ /dev/null @@ -1 +0,0 @@ -.seq \ No newline at end of file diff --git a/x.sizes b/x.sizes deleted file mode 120000 index 4e9432d..0000000 --- a/x.sizes +++ /dev/null @@ -1 +0,0 @@ -.sizes \ No newline at end of file From 87769ef0afbb14000f83844d5c9fcf1ebbb27dd0 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Mon, 14 Feb 2022 12:50:34 -0500 Subject: [PATCH 36/46] restore the conda env --- conda_setup.yml | 362 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 conda_setup.yml diff --git a/conda_setup.yml b/conda_setup.yml new file mode 100644 index 0000000..8457238 --- /dev/null +++ b/conda_setup.yml @@ -0,0 +1,362 @@ +name: lepwrap +channels: + - conda-forge/label/main + - bioconda + - conda-forge + - defaults + - r +dependencies: + - _libgcc_mutex=0.1=conda_forge + - _openmp_mutex=4.5=1_gnu + - _r-mutex=1.0.1=anacondar_1 + - aioeasywebdav=2.4.0=py39hf3d152e_1001 + - aiohttp=3.7.4=py39h3811e60_0 + - amply=0.1.4=py_0 + - appdirs=1.4.4=pyh9f0ad1d_0 + - async-timeout=3.0.1=py_1000 + - atk-1.0=2.36.0=h3371d22_4 + - attmap=0.13.0=pyhd8ed1ab_0 + - attrs=21.2.0=pyhd8ed1ab_0 + - azure-common=1.1.27=pyhd8ed1ab_0 + - azure-core=1.14.0=pyhd8ed1ab_0 + - azure-storage-blob=12.8.1=pyhd8ed1ab_0 + - backports=1.0=py_2 + - backports.functools_lru_cache=1.6.4=pyhd8ed1ab_0 + - bcrypt=3.2.0=py39h3811e60_1 + - binutils_impl_linux-64=2.35.1=h193b22a_2 + - binutils_linux-64=2.35=h67ddf6f_30 + - blinker=1.4=py_1 + - boto=2.49.0=py_0 + - boto3=1.17.76=pyhd8ed1ab_0 + - botocore=1.20.76=pyhd8ed1ab_0 + - brotlipy=0.7.0=py39h3811e60_1001 + - bwidget=1.9.14=ha770c72_0 + - bzip2=1.0.8=h7f98852_4 + - c-ares=1.17.1=h7f98852_1 + - ca-certificates=2020.12.5=ha878542_0 + - cachetools=4.2.2=pyhd8ed1ab_0 + - cairo=1.16.0=h6cf1ce9_1008 + - certifi=2020.12.5=py39hf3d152e_1 + - cffi=1.14.5=py39he32792d_0 + - chardet=4.0.0=py39hf3d152e_1 + - coincbc=2.10.5=hcee13e7_1 + - configargparse=1.4.1=pyhd8ed1ab_0 + - connection_pool=0.0.3=pyhd3deb0d_0 + - cryptography=3.4.7=py39hbca0aa6_0 + - curl=7.76.1=hea6ffbf_2 + - datrie=0.8.2=py39h3811e60_2 + - decorator=5.0.9=pyhd8ed1ab_0 + - docutils=0.17.1=py39hf3d152e_0 + - dropbox=11.9.0=pyhd8ed1ab_0 + - expat=2.3.0=h9c3ff4c_0 + - fftw=3.3.9=nompi_h74d3f13_101 + - filechunkio=1.8=py_2 + - filelock=3.0.12=pyh9f0ad1d_0 + - font-ttf-dejavu-sans-mono=2.37=hab24e00_0 + - font-ttf-inconsolata=3.000=h77eed37_0 + - font-ttf-source-code-pro=2.038=h77eed37_0 + - font-ttf-ubuntu=0.83=hab24e00_0 + - fontconfig=2.13.1=hba837de_1005 + - fonts-conda-ecosystem=1=0 + - fonts-conda-forge=1=0 + - freetype=2.10.4=h0708190_1 + - fribidi=1.0.10=h36c2ea0_0 + - ftputil=5.0.1=pyhd8ed1ab_0 + - gcc_impl_linux-64=9.3.0=h70c0ae5_19 + - gcc_linux-64=9.3.0=hf25ea35_30 + - gdk-pixbuf=2.42.6=h04a7f16_0 + - gettext=0.19.8.1=h0b5b191_1005 + - gfortran_impl_linux-64=9.3.0=hc4a2995_19 + - gfortran_linux-64=9.3.0=hdc58fab_30 + - ghostscript=9.18=1 + - giflib=5.2.1=h36c2ea0_2 + - gitdb=4.0.7=pyhd8ed1ab_0 + - gitpython=3.1.17=pyhd8ed1ab_0 + - google-api-core=1.26.3=pyhd8ed1ab_0 + - google-api-python-client=2.5.0=pyhd8ed1ab_0 + - google-auth=1.30.0=pyh44b312d_0 + - google-auth-httplib2=0.1.0=pyhd8ed1ab_0 + - google-cloud-core=1.5.0=pyhd3deb0d_0 + - google-cloud-storage=1.19.0=py_0 + - google-crc32c=1.1.2=py39hb81f231_0 + - google-resumable-media=1.2.0=pyhd3deb0d_0 + - googleapis-common-protos=1.53.0=py39hf3d152e_0 + - graphite2=1.3.13=h58526e2_1001 + - graphviz=2.47.1=h85b4f2f_1 + - grpcio=1.37.1=py39hff7568b_0 + - gsl=2.6=he838d99_2 + - gtk2=2.24.33=h539f30e_1 + - gts=0.7.6=h64030ff_2 + - gxx_impl_linux-64=9.3.0=hd87eabc_19 + - gxx_linux-64=9.3.0=h3fbe746_30 + - harfbuzz=2.8.1=h83ec7ef_0 + - httplib2=0.19.1=pyhd8ed1ab_0 + - icu=68.1=h58526e2_0 + - idna=2.10=pyh9f0ad1d_0 + - imagemagick=7.0.11_13=pl5320hb118871_0 + - importlib-metadata=4.0.1=py39hf3d152e_0 + - iniconfig=1.1.1=pyh9f0ad1d_0 + - ipython_genutils=0.2.0=py_1 + - isodate=0.6.0=py_1 + - jbig=2.1=h7f98852_2003 + - jinja2=3.0.1=pyhd8ed1ab_0 + - jmespath=0.10.0=pyh9f0ad1d_0 + - jpeg=9d=h36c2ea0_0 + - jsonschema=3.2.0=pyhd8ed1ab_3 + - jupyter_core=4.7.1=py39hf3d152e_0 + - kernel-headers_linux-64=2.6.32=h77966d4_13 + - krb5=1.19.1=hcc1bbae_0 + - ld_impl_linux-64=2.35.1=hea4e1c9_2 + - libblas=3.9.0=9_openblas + - libcblas=3.9.0=9_openblas + - libcrc32c=1.1.1=h9c3ff4c_2 + - libcurl=7.76.1=h2574ce0_2 + - libedit=3.1.20191231=he28a2e2_2 + - libev=4.33=h516909a_1 + - libffi=3.3=h58526e2_2 + - libgcc=7.2.0=h69d50b8_2 + - libgcc-devel_linux-64=9.3.0=h7864c58_19 + - libgcc-ng=9.3.0=h2828fa1_19 + - libgd=2.3.2=h78a0170_0 + - libgfortran-ng=9.3.0=hff62375_19 + - libgfortran5=9.3.0=hff62375_19 + - libglib=2.68.2=h3e27bee_0 + - libgomp=9.3.0=h2828fa1_19 + - libiconv=1.16=h516909a_0 + - liblapack=3.9.0=9_openblas + - libnghttp2=1.43.0=h812cca2_0 + - libopenblas=0.3.15=pthreads_h8fe5266_1 + - libpng=1.6.37=h21135ba_2 + - libprotobuf=3.17.0=h780b84a_0 + - librsvg=2.50.5=hc3c00ef_0 + - libsodium=1.0.18=h36c2ea0_1 + - libssh2=1.9.0=ha56f1ee_6 + - libstdcxx-devel_linux-64=9.3.0=hb016644_19 + - libstdcxx-ng=9.3.0=h6de172a_19 + - libtiff=4.3.0=hf544144_0 + - libtool=2.4.6=h58526e2_1007 + - libuuid=2.32.1=h7f98852_1000 + - libwebp=1.2.0=h3452ae3_0 + - libwebp-base=1.2.0=h7f98852_2 + - libxcb=1.13=h7f98852_1003 + - libxml2=2.9.12=h72842e0_0 + - logmuse=0.2.6=pyh8c360ce_0 + - lz4-c=1.9.3=h9c3ff4c_0 + - make=4.3=hd18ef5c_1 + - markupsafe=2.0.1=py39h3811e60_0 + - more-itertools=8.7.0=pyhd8ed1ab_1 + - msrest=0.6.21=pyh44b312d_0 + - multidict=5.1.0=py39h3811e60_1 + - nbformat=5.1.3=pyhd8ed1ab_0 + - ncurses=6.2=h58526e2_4 + - networkx=2.5=py_0 + - numpy=1.20.3=py39hdbf815f_0 + - oauth2client=4.1.3=py_0 + - oauthlib=3.0.1=py_0 + - openjpeg=2.4.0=hb52868f_1 + - openssl=1.1.1k=h7f98852_0 + - packaging=20.9=pyh44b312d_0 + - pandas=1.2.4=py39hde0f152_0 + - pandoc=2.13=h7f98852_0 + - pango=1.48.5=hb8ff022_0 + - paramiko=2.7.2=pyh9f0ad1d_0 + - pcre=8.44=he1b5a44_0 + - pcre2=10.36=h032f7d1_1 + - peppy=0.31.1=pyhd8ed1ab_0 + - perl=5.32.0=h36c2ea0_0 + - pip=21.1.1=pyhd8ed1ab_0 + - pixman=0.40.0=h36c2ea0_0 + - pkg-config=0.29.2=h36c2ea0_1008 + - pluggy=0.13.1=py39hf3d152e_4 + - ply=3.11=py_1 + - prettytable=2.1.0=pyhd8ed1ab_0 + - protobuf=3.17.0=py39he80948d_0 + - psutil=5.8.0=py39h3811e60_1 + - pthread-stubs=0.4=h36c2ea0_1001 + - pulp=2.4=py39hf3d152e_0 + - py=1.10.0=pyhd3deb0d_0 + - pyasn1=0.4.8=py_0 + - pyasn1-modules=0.2.7=py_0 + - pycparser=2.20=pyh9f0ad1d_2 + - pygments=2.9.0=pyhd8ed1ab_0 + - pygraphviz=1.7=py39h78163bd_0 + - pyjwt=2.1.0=pyhd8ed1ab_0 + - pynacl=1.4.0=py39h3811e60_2 + - pyopenssl=20.0.1=pyhd8ed1ab_0 + - pyparsing=2.4.7=pyh9f0ad1d_0 + - pyrsistent=0.17.3=py39h3811e60_2 + - pysftp=0.2.9=py_1 + - pysocks=1.7.1=py39hf3d152e_3 + - pytest=6.2.4=py39hf3d152e_0 + - python=3.9.4=hffdb5ce_0_cpython + - python-dateutil=2.8.1=py_0 + - python-irodsclient=0.9.0=pyhd8ed1ab_0 + - python_abi=3.9=1_cp39 + - pytz=2021.1=pyhd8ed1ab_0 + - pyyaml=5.4.1=py39h3811e60_0 + - r-askpass=1.1=r40hcdcec82_2 + - r-assertthat=0.2.1=r40h6115d3f_2 + - r-backports=1.2.1=r40hcfec24a_0 + - r-base=4.0.5=h9e01966_1 + - r-base64enc=0.1_3=r40hcdcec82_1004 + - r-blob=1.2.1=r40h6115d3f_1 + - r-brio=1.1.2=r40hcfec24a_0 + - r-broom=0.7.6=r40hc72bb7e_0 + - r-callr=3.7.0=r40hc72bb7e_0 + - r-cellranger=1.1.0=r40h6115d3f_1003 + - r-cli=2.5.0=r40hc72bb7e_0 + - r-clipr=0.7.1=r40h142f84f_0 + - r-colorspace=2.0_1=r40hcfec24a_0 + - r-cpp11=0.2.7=r40hc72bb7e_0 + - r-crayon=1.4.1=r40hc72bb7e_0 + - r-curl=4.3.1=r40hcfec24a_0 + - r-data.table=1.14.0=r40hcfec24a_0 + - r-dbi=1.1.1=r40hc72bb7e_0 + - r-dbplyr=2.1.1=r40hc72bb7e_0 + - r-desc=1.3.0=r40hc72bb7e_0 + - r-diffobj=0.3.4=r40hcfec24a_0 + - r-digest=0.6.27=r40h03ef668_0 + - r-dplyr=1.0.6=r40h03ef668_1 + - r-dtplyr=1.1.0=r40hc72bb7e_0 + - r-ellipsis=0.3.2=r40hcfec24a_0 + - r-evaluate=0.14=r40h6115d3f_2 + - r-fansi=0.4.2=r40hcfec24a_0 + - r-farver=2.1.0=r40h03ef668_0 + - r-forcats=0.5.1=r40hc72bb7e_0 + - r-fs=1.5.0=r40h0357c0b_0 + - r-fuzzyjoin=0.1.6=r40_0 + - r-gargle=1.1.0=r40hc72bb7e_0 + - r-gdtools=0.2.2=r40h36050f4_1 + - r-generics=0.1.0=r40hc72bb7e_0 + - r-geosphere=1.5_10=r40hcfec24a_2 + - r-ggplot2=3.3.3=r40hc72bb7e_0 + - r-glue=1.4.2=r40hcfec24a_0 + - r-googledrive=1.0.1=r40h6115d3f_1 + - r-googlesheets4=0.3.0=r40hc72bb7e_0 + - r-gtable=0.3.0=r40h6115d3f_3 + - r-haven=2.4.1=r40h2713e49_0 + - r-highr=0.9=r40hc72bb7e_0 + - r-hms=1.1.0=r40hc72bb7e_0 + - r-htmltools=0.5.1.1=r40h03ef668_0 + - r-httr=1.4.2=r40h6115d3f_0 + - r-ids=1.0.1=r40h6115d3f_1 + - r-isoband=0.2.4=r40h03ef668_0 + - r-jsonlite=1.7.2=r40hcfec24a_0 + - r-knitr=1.33=r40hc72bb7e_0 + - r-labeling=0.4.2=r40h142f84f_0 + - r-lattice=0.20_44=r40hcfec24a_0 + - r-lifecycle=1.0.0=r40hc72bb7e_0 + - r-lubridate=1.7.10=r40h03ef668_0 + - r-magrittr=2.0.1=r40hcfec24a_1 + - r-markdown=1.1=r40hcfec24a_1 + - r-mass=7.3_54=r40hcfec24a_0 + - r-matrix=1.3_3=r40he454529_0 + - r-mgcv=1.8_35=r40he454529_0 + - r-mime=0.10=r40hcfec24a_0 + - r-modelr=0.1.8=r40h6115d3f_0 + - r-munsell=0.5.0=r40h6115d3f_1003 + - r-nlme=3.1_152=r40h859d828_0 + - r-openssl=1.4.4=r40he36bf35_0 + - r-pillar=1.6.1=r40hc72bb7e_0 + - r-pkgconfig=2.0.3=r40h6115d3f_1 + - r-pkgload=1.2.1=r40h03ef668_0 + - r-plyr=1.8.6=r40h0357c0b_1 + - r-praise=1.0.0=r40h6115d3f_1004 + - r-prettyunits=1.1.1=r40h6115d3f_1 + - r-processx=3.5.2=r40hcfec24a_0 + - r-progress=1.2.2=r40h6115d3f_2 + - r-ps=1.6.0=r40hcfec24a_0 + - r-purrr=0.3.4=r40hcfec24a_1 + - r-r6=2.5.0=r40hc72bb7e_0 + - r-rappdirs=0.3.3=r40hcfec24a_0 + - r-rcolorbrewer=1.1_2=r40h6115d3f_1003 + - r-rcpp=1.0.6=r40h03ef668_0 + - r-readr=1.4.0=r40h1b71b39_0 + - r-readxl=1.3.1=r40hde08347_4 + - r-rematch=1.0.1=r40h6115d3f_1003 + - r-rematch2=2.1.2=r40h6115d3f_1 + - r-reprex=2.0.0=r40hc72bb7e_0 + - r-reshape2=1.4.4=r40h0357c0b_1 + - r-rlang=0.4.11=r40hcfec24a_0 + - r-rmarkdown=2.8=r40hc72bb7e_0 + - r-rprojroot=2.0.2=r40hc72bb7e_0 + - r-rstudioapi=0.13=r40hc72bb7e_0 + - r-rvest=1.0.0=r40hc72bb7e_0 + - r-scales=1.1.1=r40h6115d3f_0 + - r-selectr=0.4_2=r40h6115d3f_1 + - r-sp=1.4_5=r40hcfec24a_0 + - r-stringdist=0.9.6.3=r40hcfec24a_0 + - r-stringi=1.6.2=r40hcabe038_0 + - r-stringr=1.4.0=r40h6115d3f_2 + - r-svglite=2.0.0=r40h03ef668_0 + - r-sys=3.4=r40hcdcec82_0 + - r-systemfonts=1.0.2=r40hef9c87a_0 + - r-testthat=3.0.2=r40h03ef668_0 + - r-tibble=3.1.2=r40hcfec24a_0 + - r-tidyr=1.1.3=r40h03ef668_0 + - r-tidyselect=1.1.1=r40hc72bb7e_0 + - r-tidyverse=1.3.1=r40hc72bb7e_0 + - r-tinytex=0.31=r40hc72bb7e_0 + - r-utf8=1.2.1=r40hcfec24a_0 + - r-uuid=0.1_4=r40hcdcec82_1 + - r-vctrs=0.3.8=r40hcfec24a_0 + - r-viridislite=0.4.0=r40hc72bb7e_0 + - r-waldo=0.2.5=r40hc72bb7e_0 + - r-withr=2.4.2=r40hc72bb7e_0 + - r-xfun=0.23=r40hcfec24a_0 + - r-xml2=1.3.2=r40h0357c0b_1 + - r-yaml=2.2.1=r40hcfec24a_1 + - r-zeallot=0.1.0=r40h6115d3f_1002 + - ratelimiter=1.2.0=py_1002 + - readline=8.1=h46c0cb4_0 + - requests=2.25.1=pyhd3deb0d_0 + - requests-oauthlib=1.3.0=pyh9f0ad1d_0 + - rsa=4.7.2=pyh44b312d_0 + - s3transfer=0.4.2=pyhd8ed1ab_0 + - sed=4.8=he412f7d_0 + - setuptools=49.6.0=py39hf3d152e_3 + - simplejson=3.17.2=py39h3811e60_2 + - six=1.16.0=pyh6c4a22f_0 + - slacker=0.14.0=py_0 + - smart_open=5.0.0=pyhd8ed1ab_0 + - smmap=3.0.5=pyh44b312d_0 + - snakemake=6.4.0=hdfd78af_0 + - snakemake-minimal=6.4.0=pyhdfd78af_0 + - sqlite=3.35.5=h74cdb3f_0 + - stone=3.2.1=pyhd8ed1ab_0 + - stopit=1.1.2=py_0 + - sysroot_linux-64=2.12=h77966d4_13 + - tk=8.6.10=h21135ba_1 + - tktable=2.10=hb7b940f_3 + - toml=0.10.2=pyhd8ed1ab_0 + - toposort=1.6=pyhd8ed1ab_0 + - traitlets=5.0.5=py_0 + - typing-extensions=3.7.4.3=0 + - typing_extensions=3.7.4.3=py_0 + - tzdata=2021a=he74cb21_0 + - ubiquerg=0.6.1=pyh9f0ad1d_0 + - uritemplate=3.0.1=py_0 + - urllib3=1.26.4=pyhd8ed1ab_0 + - veracitools=0.1.3=py_0 + - wcwidth=0.2.5=pyh9f0ad1d_2 + - wheel=0.36.2=pyhd3deb0d_0 + - wrapt=1.12.1=py39h3811e60_3 + - xmlrunner=1.7.7=py_0 + - xorg-kbproto=1.0.7=h7f98852_1002 + - xorg-libice=1.0.10=h7f98852_0 + - xorg-libsm=1.2.3=hd9c2040_1000 + - xorg-libx11=1.7.1=h7f98852_0 + - xorg-libxau=1.0.9=h7f98852_0 + - xorg-libxdmcp=1.1.3=h7f98852_0 + - xorg-libxext=1.3.4=h7f98852_1 + - xorg-libxrender=0.9.10=h7f98852_1003 + - xorg-libxt=1.2.1=h7f98852_2 + - xorg-renderproto=0.11.1=h7f98852_1002 + - xorg-xextproto=7.3.0=h7f98852_1002 + - xorg-xproto=7.0.31=h7f98852_1007 + - xz=5.2.5=h516909a_1 + - yaml=0.2.5=h516909a_0 + - yarl=1.6.3=py39h3811e60_1 + - zipp=3.4.1=pyhd8ed1ab_0 + - zlib=1.2.11=h516909a_1010 + - zstd=1.5.0=ha95c52a_0 From 01b8a7e3d9f1fe442b688cb5a1ede7b6338e6474 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Mon, 14 Feb 2022 13:27:54 -0500 Subject: [PATCH 37/46] update readme --- .misc/install.sh | 25 +++++++++++++++++++++++++ .misc/meta.yml | 6 +++++- README.md | 29 ++++++++++++++++++++++------- 3 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 .misc/install.sh diff --git a/.misc/install.sh b/.misc/install.sh new file mode 100644 index 0000000..c186161 --- /dev/null +++ b/.misc/install.sh @@ -0,0 +1,25 @@ +#! /usr/bin/env bash + +if [ -z "$CONDA_PREFIX" ]; then + echo "No active conda environment detected, will not install dependencies unless in an active environment" + exit 1 +fi + +# install LepWrap into conda PATH +mkdir -p $CONDA_PREFIX/bin +# LepWrap executable +cp LepWrap $CONDA_PREFIX/bin/ +chmod +x $CONDA_PREFIX/bin/LepWrap +# associated scripts +chmod +x scripts/* +cp scripts/* $CONDA_PREFIX/bin/ +# LepMap3 modules and scripts +cp software/LepMap3/*.class $CONDA_PREFIX/bin +cp software/LepMap3/scripts/* $CONDA_PREFIX/bin +# LepAnchor modules and scripts +cp software/LepAnchor/*.class $CONDA_PREFIX/bin +cp software/LepAnchor/scripts/* $CONDA_PREFIX/bin +cp software/LepAnchor/deps/ucsc_binaries/* $CONDA_PREFIX/bin +cp software/LepAnchor/deps/*.pl software/LepAnchor/deps/Red software/LepAnchor/deps/all_lastz.ctl software/LepAnchor/deps/scoreMatrix.q software/LepAnchor/deps/step* $CONDA_PREFIX/bin +# Snakemake rules +cp rules/LepAnchor/*.smk rules/LepMap3/*.smk $CONDA_PREFIX/bin \ No newline at end of file diff --git a/.misc/meta.yml b/.misc/meta.yml index 26e8868..15c625f 100644 --- a/.misc/meta.yml +++ b/.misc/meta.yml @@ -27,7 +27,11 @@ requirements: - pandoc - python >=3.9 - r-base >=4 - - r-tidyverse + - r-dplyr + - r-tidyr + - r-stringr + - r-ggplot2 + - r-readr - sed - snakemake >=6.4 diff --git a/README.md b/README.md index edc77ae..188acaf 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ _It's Lep-Map3 and Lep-Anchor, but with snakes 🐍🐍_ [![alt text](https://img.shields.io/badge/docs-wiki-75ae6c?style=for-the-badge&logo=Read%20The%20Docs)](https://github.com/pdimens/LepWrap/wiki) +[![Cite](https://img.shields.io/badge/Cite-10.5281/zenodo.6055566-e1e1e1?style=for-the-badge)](https://zenodo.org/badge/latestdoi/260516189) # LepWrap LepWrap is a reusable pipeline to use the linkage map software [Lep-Map3](https://sourceforge.net/projects/lep-map3/) and the genome assembly map-based anchoring and orienting software [Lep-Anchor](https://sourceforge.net/p/lep-anchor/wiki/Home/). It is the Snakemake-based successor to [LepMapp3r](https://github.com/pdimens/LepMapp3r). Check out [the wiki](https://github.com/pdimens/LepWrap/wiki) for detailed installation, usage, and workflow information. @@ -11,15 +12,23 @@ LepWrap is a reusable pipeline to use the linkage map software [Lep-Map3](https: ### How to install -You will need a `conda` installation ([Anaconda](https://docs.anaconda.com/anaconda/install/) or [Miniconda](https://docs.conda.io/en/latest/miniconda.html), I recommend Miniconda), along with downloading the latest release or cloning this repository locally. Using the latest release is a more stable solution. +You will need a `conda` installation ([Anaconda](https://docs.anaconda.com/anaconda/install/) or [Miniconda](https://docs.conda.io/en/latest/miniconda.html), I recommend Miniconda). Alternatively, you can download latest release or clone this repository locally. -#### 1. Cloning LepWrap +#### 1. The Easy Way™️ +Create an environment called `lepwrap` and install `LepWrap` into it in a single command. +```bash +conda create -n lepwrap -c bioconda lepwrap +``` +Activate the environment with `conda activate lepwrap` + +#### 2. The Other Way +##### 2.1 Cloning LepWrap Download a zip of this repository using the "Code" button on the top-right and unzip it on your machine or: ```bash git clone https://github.com/pdimens/LepWrap.git ``` -#### 2. Installing other dependencies +##### 2.2 Installing everything Assuming you have `anaconda` or `miniconda` installed: ```bash cd LepWrap @@ -29,25 +38,31 @@ This will create an environment called `lepwrap` that can be activated with: ```bash conda activate lepwrap ``` +Then, install all the software dependencies into the environment's path with +```bash +.misc/install.sh +``` ### How to run You will need to modify `config.yml` to suit your needs, then you can simply run the pipeline with the wrapper: ```bash -./LepWrap +LepWrap ``` where `` is an integer of the maximum number of cores/threads you want the pipeline to use and `` (optional!) is the name of the config file, if it's different than `config.yml`. **Examples** ```bash -./LepWrap 15 # assumes config.yml -./LepWrap 32 nojoinsingles.yml # specific config file +LepWrap 15 # assumes config.yml +LepWrap 32 nojoinsingles.yml # specific config file ``` ### Something to keep in mind LepWrap does things a certain way, employing the most common/reasonable way of using Lep-Map3 (and LepAnchor more or less). Current versions are **a lot** more flexible that the predecessors, but might still lack something you need. Your study is unique, and I encourage you to clone/fork this repository and adapt LepWrap to it! All of the code in LepWrap is written in human-readable bash or aggressively annotated R, so give it a shot and adapt it to your workflow. PR's always welcome! ## Citation -If using LepWrap in a publication, cite **Pasi Rastas** for their work on Lep-Map3/Lep-Anchor and please include a link to this repository. If you like using it, please Star the repository and/or give me (Pavel) a shout out on Twitter [@pvdimens](https://twitter.com/PVDimens) [![alt text](http://i.imgur.com/wWzX9uB.png)](https://twitter.com/PVDimens) =) +If using LepWrap in a publication, cite **Pasi Rastas** for their work on Lep-Map3/Lep-Anchor and cite this repository using the Zenodo citation. You are also encouraged to cite LepWrap if you just use the edge-trimming part of it. If you like LepWrap, please Star the repository and/or give me (Pavel) a shout out on Twitter [@pvdimens](https://twitter.com/PVDimens) [![alt text](http://i.imgur.com/wWzX9uB.png)](https://twitter.com/PVDimens) =) + +> Pavel V. Dimens. (2022). pdimens/LepWrap: link with zenodo (3.6.2). Zenodo. https://doi.org/10.5281/zenodo.6055566 > Pasi Rastas, Lep-MAP3: robust linkage mapping even for low-coverage whole genome sequencing data, Bioinformatics, Volume 33, Issue 23, 01 December 2017, Pages 3726–3732,https://doi.org/10.1093/bioinformatics/btx494 From 51310090a3c9af82498a771e00616392d1057bb5 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Mon, 14 Feb 2022 13:29:04 -0500 Subject: [PATCH 38/46] move citation inline --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 188acaf..4cb519f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ _It's Lep-Map3 and Lep-Anchor, but with snakes 🐍🐍_ [![alt text](https://img.shields.io/badge/docs-wiki-75ae6c?style=for-the-badge&logo=Read%20The%20Docs)](https://github.com/pdimens/LepWrap/wiki) - [![Cite](https://img.shields.io/badge/Cite-10.5281/zenodo.6055566-e1e1e1?style=for-the-badge)](https://zenodo.org/badge/latestdoi/260516189) # LepWrap From 18b015b031a444dce5104f52e3b214cb1308c3b0 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Mon, 14 Feb 2022 14:45:13 -0500 Subject: [PATCH 39/46] split out java software b/c of overwriting --- .misc/build.sh | 8 +++++--- .misc/install.sh | 7 +++++-- rules/LepAnchor/generate_inputs.smk | 6 +++--- rules/LepAnchor/place_orient1.smk | 2 +- rules/LepAnchor/place_orient2.smk | 2 +- rules/LepAnchor/place_orient4.smk | 2 +- rules/LepMap3/distances.smk | 4 ++-- rules/LepMap3/generate_map.smk | 4 ++-- rules/LepMap3/order.smk | 2 +- rules/LepMap3/prepare_data.smk | 4 ++-- rules/LepMap3/reorder.smk | 2 +- scripts/iterate_js2all.sh | 2 +- scripts/refinemap.sh | 2 +- 13 files changed, 26 insertions(+), 21 deletions(-) diff --git a/.misc/build.sh b/.misc/build.sh index 2ba015f..56ae559 100644 --- a/.misc/build.sh +++ b/.misc/build.sh @@ -2,6 +2,8 @@ # for installing conda package mkdir -p $PREFIX/bin +mkdir -p $PREFIX/bin/lepmap3 +mkdir -p $PREFIX/bin/lepanchor # LepWrap executable cp LepWrap $PREFIX/bin/ chmod +x $PREFIX/bin/LepWrap @@ -9,11 +11,11 @@ chmod +x $PREFIX/bin/LepWrap chmod +x scripts/* cp scripts/* $PREFIX/bin/ # LepMap3 modules and scripts -cp software/LepMap3/*.class $PREFIX/bin +cp software/LepMap3/*.class $PREFIX/bin/lepmap3 cp software/LepMap3/scripts/* $PREFIX/bin # LepAnchor modules and scripts -cp software/LepAnchor/*.class software/LepAnchor/lepanchor_wrapper.sh $PREFIX/bin -cp $CONDA_PREFIX/bin/* $PREFIX/bin +cp software/LepAnchor/*.class $PREFIX/bin/lepanchor +cp software/LepAnchor/scripts/* $PREFIX/bin cp software/LepAnchor/deps/ucsc_binaries/* $PREFIX/bin cp software/LepAnchor/deps/*.pl software/LepAnchor/deps/Red software/LepAnchor/deps/all_lastz.ctl software/LepAnchor/deps/scoreMatrix.q software/LepAnchor/deps/step* $PREFIX/bin # Snakemake rules diff --git a/.misc/install.sh b/.misc/install.sh index c186161..f791fdc 100644 --- a/.misc/install.sh +++ b/.misc/install.sh @@ -7,6 +7,8 @@ fi # install LepWrap into conda PATH mkdir -p $CONDA_PREFIX/bin +mkdir -p $CONDA_PREFIX/bin/lepmap3 +mkdir -p $CONDA_PREFIX/bin/lepanchor # LepWrap executable cp LepWrap $CONDA_PREFIX/bin/ chmod +x $CONDA_PREFIX/bin/LepWrap @@ -14,11 +16,12 @@ chmod +x $CONDA_PREFIX/bin/LepWrap chmod +x scripts/* cp scripts/* $CONDA_PREFIX/bin/ # LepMap3 modules and scripts -cp software/LepMap3/*.class $CONDA_PREFIX/bin +cp software/LepMap3/*.class $CONDA_PREFIX/bin/lepmap3 cp software/LepMap3/scripts/* $CONDA_PREFIX/bin # LepAnchor modules and scripts -cp software/LepAnchor/*.class $CONDA_PREFIX/bin +cp software/LepAnchor/*.class $CONDA_PREFIX/bin/lepanchor cp software/LepAnchor/scripts/* $CONDA_PREFIX/bin +ln -s $CONDA_PREFIX/bin/lepmap3/*.class $CONDA_PREFIX/bin/lepanchor/*.class $CONDA_PREFIX/bin/ cp software/LepAnchor/deps/ucsc_binaries/* $CONDA_PREFIX/bin cp software/LepAnchor/deps/*.pl software/LepAnchor/deps/Red software/LepAnchor/deps/all_lastz.ctl software/LepAnchor/deps/scoreMatrix.q software/LepAnchor/deps/step* $CONDA_PREFIX/bin # Snakemake rules diff --git a/rules/LepAnchor/generate_inputs.smk b/rules/LepAnchor/generate_inputs.smk index f1e3aa8..96b3dd2 100644 --- a/rules/LepAnchor/generate_inputs.smk +++ b/rules/LepAnchor/generate_inputs.smk @@ -52,7 +52,7 @@ rule liftover: message: "Running liftoverHaplotypes for the input maps" shell: """ - gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ LiftoverHaplotypes map={input.intervals} haplotypes={input.haplos} chain=- > {output.lift} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/lepanchor LiftoverHaplotypes map={input.intervals} haplotypes={input.haplos} chain=- > {output.lift} cat {output.lift} | sort -V -k 1,1 -k 2,2n > {output.sortedlift} """ @@ -64,7 +64,7 @@ rule cleanmap: message: "Running CleanMap" params: extras = cleanmap_extra - shell: "java -cp $CONDA_PREFIX/bin/ CleanMap map={input} {params.extras} > {output} 2> {log}" + shell: "java -cp $CONDA_PREFIX/bin/lepanchor CleanMap map={input} {params.extras} > {output} 2> {log}" rule map2bed: @@ -76,7 +76,7 @@ rule map2bed: message: "Running Map2Bed" params: extras = map2bed_extra - shell: "java -cp $CONDA_PREFIX/bin/ Map2Bed map={input.cleanmap} contigLength={input.lengths} {params.extras} > {output} 2> {log}" + shell: "java -cp $CONDA_PREFIX/bin/lepanchor Map2Bed map={input.cleanmap} contigLength={input.lengths} {params.extras} > {output} 2> {log}" rule ungrouped: diff --git a/rules/LepAnchor/place_orient1.smk b/rules/LepAnchor/place_orient1.smk index 4b97cca..5f024aa 100644 --- a/rules/LepAnchor/place_orient1.smk +++ b/rules/LepAnchor/place_orient1.smk @@ -16,7 +16,7 @@ rule place_orient: message: "Running the 1st round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ - gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/lepanchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} """ rule propogate1: diff --git a/rules/LepAnchor/place_orient2.smk b/rules/LepAnchor/place_orient2.smk index b6ad01d..243cef8 100644 --- a/rules/LepAnchor/place_orient2.smk +++ b/rules/LepAnchor/place_orient2.smk @@ -17,7 +17,7 @@ rule place_orient2: message: "Running 2nd round of PlaceAndOrientContigs for linkage group {params.chrom}" shell: """ - gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f $CONDA_PREFIX/bin/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/lepanchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f $CONDA_PREFIX/bin/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} > {output.chrom} 2> {output.chromerr} """ rule propogate2: diff --git a/rules/LepAnchor/place_orient4.smk b/rules/LepAnchor/place_orient4.smk index 1640b17..e86a093 100644 --- a/rules/LepAnchor/place_orient4.smk +++ b/rules/LepAnchor/place_orient4.smk @@ -18,7 +18,7 @@ rule place_orient4: threads: 2 shell: """ - gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/ PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f $CONDA_PREFIX/bin/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/lepanchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f $CONDA_PREFIX/bin/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} {params.datatype} {params.extras} evaluateAnchoring={input.chromlast} improveAnchoring=1 > {output.chrom} 2> {output.err} """ rule prune_contigblocks: diff --git a/rules/LepMap3/distances.smk b/rules/LepMap3/distances.smk index 65ce7f7..0807e7a 100644 --- a/rules/LepMap3/distances.smk +++ b/rules/LepMap3/distances.smk @@ -19,10 +19,10 @@ rule calculate_distances: """ cp {input.lg} {output.distance} - zcat {input.data_call} | java -cp $CONDA_PREFIX/bin/ OrderMarkers2 evaluateOrder={input.lg} data=- {params.dist_method} numThreads={threads} improveOrder=0 sexAveraged=1 &> {output.sex_averagedtmp} + zcat {input.data_call} | java -cp $CONDA_PREFIX/bin/lepmap3 OrderMarkers2 evaluateOrder={input.lg} data=- {params.dist_method} numThreads={threads} improveOrder=0 sexAveraged=1 &> {output.sex_averagedtmp} sed -i -e 's/LG \= 0/LG \= {params.lg}/g' {output.sex_averagedtmp} sed -n '/\*\*\* LG \=/,$p' {output.sex_averagedtmp} > {output.sex_averaged} awk '/#java/{{flag=1}} flag; /*** LG =/{{flag=0}}' {output.sex_averagedtmp} > {log.sex_averaged} - zcat {input.data_call} | java -cp $CONDA_PREFIX/bin/ OrderMarkers2 evaluateOrder={input.lg} data=- {params.dist_method} numThreads={threads} calculateIntervals={output.intervals} > {log.intervals} 2>&1 + zcat {input.data_call} | java -cp $CONDA_PREFIX/bin/lepmap3 OrderMarkers2 evaluateOrder={input.lg} data=- {params.dist_method} numThreads={threads} calculateIntervals={output.intervals} > {log.intervals} 2>&1 """ \ No newline at end of file diff --git a/rules/LepMap3/generate_map.smk b/rules/LepMap3/generate_map.smk index 092e4bf..3356fc8 100644 --- a/rules/LepMap3/generate_map.smk +++ b/rules/LepMap3/generate_map.smk @@ -9,7 +9,7 @@ rule separate_chromosomes: extra = sepchrom_extra, shell: """ - zcat {input} | java -cp $CONDA_PREFIX/bin/ SeparateChromosomes2 lodLimit={params.lod} data=- {params.extra} {informative} numThreads={threads} > {output} 2> {log} + zcat {input} | java -cp $CONDA_PREFIX/bin/lepmap3 SeparateChromosomes2 lodLimit={params.lod} data=- {params.extra} {informative} numThreads={threads} > {output} 2> {log} """ @@ -51,7 +51,7 @@ rule join_singles: JS2A=$(echo {params.run_js2all} | tr '[:upper:]' '[:lower:]') THEMAP=$(tail -1 {input.map_choice}) if [ $JS2A == "true" ]; then - zcat {input.datacall} | java -cp $CONDA_PREFIX/bin/ JoinSingles2All map=$THEMAP data=- {params.extra} {params.lod_limit} {params.lod_diff} numThreads={threads} > {output} + zcat {input.datacall} | java -cp $CONDA_PREFIX/bin/lepmap3 JoinSingles2All map=$THEMAP data=- {params.extra} {params.lod_limit} {params.lod_diff} numThreads={threads} > {output} else echo -e "\nSkipping JoinSingles2All and creating a symlink to $THEMAP instead" ln -sr $THEMAP {output} diff --git a/rules/LepMap3/order.smk b/rules/LepMap3/order.smk index d0a5c52..4643962 100644 --- a/rules/LepMap3/order.smk +++ b/rules/LepMap3/order.smk @@ -15,7 +15,7 @@ rule order_markers: threads: 2 shell: """ - zcat {input.datacall} | java -cp $CONDA_PREFIX/bin/ OrderMarkers2 chromosome={params.chrom} map={input.filt_map} {params.extra} data=- numThreads={threads} &> {output.runlog} + zcat {input.datacall} | java -cp $CONDA_PREFIX/bin/lepmap3 OrderMarkers2 chromosome={params.chrom} map={input.filt_map} {params.extra} data=- numThreads={threads} &> {output.runlog} sed -n '/\*\*\* LG \=/,$p' {output.runlog} > {output.lg} grep "recombin" {output.runlog} > {log.recomb} awk '/#java/{{flag=1}} flag; /logL/{{flag=0}}' {output.runlog} > {log.run} diff --git a/rules/LepMap3/prepare_data.smk b/rules/LepMap3/prepare_data.smk index bbcac5e..c066a77 100644 --- a/rules/LepMap3/prepare_data.smk +++ b/rules/LepMap3/prepare_data.smk @@ -6,7 +6,7 @@ rule parent_call: message: "Creating Lep-Map3 data file from {input.vcf} and {input.pedigree}" params: extra = parentcall_extra - shell: "java -cp $CONDA_PREFIX/bin/ ParentCall2 data={input.pedigree} vcfFile={input.vcf} {params} | gzip > {output}" + shell: "java -cp $CONDA_PREFIX/bin/lepmap3lepmap3lepmap3 ParentCall2 data={input.pedigree} vcfFile={input.vcf} {params} | gzip > {output}" rule filtering: input: "1_ParentCall/data.lepmap3.gz" @@ -21,6 +21,6 @@ rule filtering: echo "Skipping Filtering2 and creating symlink {output} instead" ln -sr {input} {output} else - zcat {input} | java -cp $CONDA_PREFIX/bin/ Filtering2 data=- dataTolerance={params.data_tolerance} {params.extra} | gzip > {output} + zcat {input} | java -cp $CONDA_PREFIX/bin/lepmap3 Filtering2 data=- dataTolerance={params.data_tolerance} {params.extra} | gzip > {output} fi """ diff --git a/rules/LepMap3/reorder.smk b/rules/LepMap3/reorder.smk index f5654b2..154ade2 100644 --- a/rules/LepMap3/reorder.smk +++ b/rules/LepMap3/reorder.smk @@ -16,7 +16,7 @@ rule reorder_markers: threads: 2 shell: """ - zcat {input.datacall} | java -cp $CONDA_PREFIX/bin/ OrderMarkers2 evaluateOrder={input.lg_order} {params.extra} map={input.filt_map} data=- numThreads={threads} &> {output.runlog} + zcat {input.datacall} | java -cp $CONDA_PREFIX/bin/lepmap3 OrderMarkers2 evaluateOrder={input.lg_order} {params.extra} map={input.filt_map} data=- numThreads={threads} &> {output.runlog} sed -n '/\*\*\* LG \=/,$p' {output.runlog} > {output.lg} grep "recombin" {output.runlog} > {log.recomb} awk '/#java/{{flag=1}} flag; /logL/{{flag=0}}' {output.runlog} > {log.run} diff --git a/scripts/iterate_js2all.sh b/scripts/iterate_js2all.sh index dec5dac..fee7cf3 100755 --- a/scripts/iterate_js2all.sh +++ b/scripts/iterate_js2all.sh @@ -32,7 +32,7 @@ else fi for i in $(seq $LODMIN $LODMAX); do - zcat 2_Filtering/data.filtered.lepmap3.gz | java -cp $CONDA_PREFIX/bin/ JoinSingles2All map=$TARGETMAP data=- lodLimit=$i lodDifference=4 iterate=1 distortionLod=1 numThreads=10 informativeMask=$INFMASK > JoinSingles2All_iter/logs/map.$i.$4.js2all + zcat 2_Filtering/data.filtered.lepmap3.gz | java -cp $CONDA_PREFIX/bin/lepmap3lepmap3 JoinSingles2All map=$TARGETMAP data=- lodLimit=$i lodDifference=4 iterate=1 distortionLod=1 numThreads=10 informativeMask=$INFMASK > JoinSingles2All_iter/logs/map.$i.$4.js2all cut -f1 JoinSingles2All_iter/logs/map.$i.$4.js2all > JoinSingles2All_iter/LOD.$i.$4.js2all done diff --git a/scripts/refinemap.sh b/scripts/refinemap.sh index fd60344..961558f 100755 --- a/scripts/refinemap.sh +++ b/scripts/refinemap.sh @@ -31,7 +31,7 @@ LODMAX=$4 SIZELIM=$5 for i in $(seq $LODMIN $LODMAX); do - zcat 2_Filtering/data.filtered.lepmap3.gz | java -cp $CONDA_PREFIX/bin/ SeparateChromosomes2 data=- map=$TARGETMAP lg=$TARGETLG sizeLimit=$SIZELIM lodLimit=$i distortionLod=1 numThreads=4 > $1.refine/map.$i + zcat 2_Filtering/data.filtered.lepmap3.gz | java -cp $CONDA_PREFIX/bin/lepmap3 SeparateChromosomes2 data=- map=$TARGETMAP lg=$TARGETLG sizeLimit=$SIZELIM lodLimit=$i distortionLod=1 numThreads=4 > $1.refine/map.$i done # generate a summary of the results From 4005f99238741a484c47d8ed7d09be31ea488cbc Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Mon, 14 Feb 2022 14:48:10 -0500 Subject: [PATCH 40/46] fix grep target --- LepWrap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LepWrap b/LepWrap index 2921105..f770c7a 100755 --- a/LepWrap +++ b/LepWrap @@ -102,8 +102,8 @@ lepanchor(){ snakemake --cores $1 --snakefile $CONDA_PREFIX/bin/LepAnchor.smk --configfile $CONF --directory . } -LM=$(grep "run_lepmap" config.yml | cut -d":" -f2 | xargs | tr '[:upper:]' '[:lower:]') -LA=$(grep "run_lepanchor" config.yml | cut -d":" -f2 | xargs | tr '[:upper:]' '[:lower:]') +LM=$(grep "run_lepmap" $CONF | cut -d":" -f2 | xargs | tr '[:upper:]' '[:lower:]') +LA=$(grep "run_lepanchor" $CONF | cut -d":" -f2 | xargs | tr '[:upper:]' '[:lower:]') if [ $LM == "true" ]; then lepmap $1 $CONF From bbf3adef6ccce7da3a9843441abad62f8e784eb3 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Mon, 14 Feb 2022 15:02:48 -0500 Subject: [PATCH 41/46] update wrapper logic --- LepWrap | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/LepWrap b/LepWrap index f770c7a..3c4f521 100755 --- a/LepWrap +++ b/LepWrap @@ -3,7 +3,11 @@ # Help text if [[ -z "$1" ]]; then echo "Perform the modules of Lep-Map3 and/or Lep-Anchor" - echo "The config file (default: config.yml) must be configured" + echo -n "A config file (default: config.yml) must be configured" + if [ ! -f config.yml ]; then + echo " (one was created for you)" + generate_config.sh > config.yml + fi echo -e "The second positional argument is optional if your config file is named config.yml\n" printf "\033[01;32m" printf "[usage]" From 7a99d5deae75b1ae15bafaed1cf0fa0e574b9e02 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Wed, 16 Feb 2022 16:33:07 -0500 Subject: [PATCH 42/46] fixes --- rules/LepAnchor/generate_inputs.smk | 2 +- rules/LepAnchor/place_orient3.smk | 49 +++++++++++++++++++++++++++++ scripts/LGcutoff.sh | 2 +- scripts/extract_markers.sh | 2 +- scripts/iterate_js2all.sh | 4 +-- scripts/lepmap2anchor | 4 +-- scripts/refinemap.sh | 4 +-- scripts/usage | 2 +- 8 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 rules/LepAnchor/place_orient3.smk diff --git a/rules/LepAnchor/generate_inputs.smk b/rules/LepAnchor/generate_inputs.smk index 96b3dd2..1151398 100644 --- a/rules/LepAnchor/generate_inputs.smk +++ b/rules/LepAnchor/generate_inputs.smk @@ -2,7 +2,7 @@ rule extract_markers: input: "2_Filtering/data.filtered.lepmap3.gz" output: report("snps.txt", category = "Data") message: "Extracting marker information from Lep-Map3 data file {input}" - shell: "scripts/extract_markers.sh {input}" + shell: "extract_markers.sh {input}" rule generate_input_data: diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk new file mode 100644 index 0000000..e72008d --- /dev/null +++ b/rules/LepAnchor/place_orient3.smk @@ -0,0 +1,49 @@ + +rule place_orient3: + input: + chain = "9_Chain/chainfile.gz", + bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", + paf = paf, + prox = proximity, + lift = "10_PlaceAndOrientContigs/liftover.la", + chrom = "10_PlaceAndOrientContigs/1_orient/chr.{lg_range}.la", + propogated = "10_PlaceAndOrientContigs/propogate/propogated.{lg_range}.la" + output: + chrom = "10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la", + errors = "10_PlaceAndOrientContigs/3_orient/errors/chr.{lg_range}.err" + params: + chrom = "{lg_range}", + extras = place_orient_extra, + datatype = data_type + message: "Running 3rd round of PlaceAndOrientContigs for linkage group {params.chrom}" + threads: 2 + shell: + """ + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} + """ + +rule prune_contigblocks: + input: "10_PlaceAndOrientContigs/3_orient/chr.{lg_range}.la" + output: + chrom = "10_PlaceAndOrientContigs/pruned/chr.{lg_range}.pruned.la", + err = "10_PlaceAndOrientContigs/pruned/err/chr.{lg_range}.pruned.err" + message: "Pruning contig blocks without map support and removing overlaps" + params: + chrom = lg + shell: "awk -f software/LepAnchor/scripts/prune.awk {input} > {output.chrom} 2> {output.err}" + +rule prune_post: + input: + bedfile = "10_PlaceAndOrientContigs/map.propogated2.bed", + prunedchrom = expand("10_PlaceAndOrientContigs/pruned/chr.{lgs}.pruned.la", lgs = lg_range), + prunederr = expand("10_PlaceAndOrientContigs/pruned/err/chr.{lgs}.pruned.err", lgs = lg_range) + output: + overlaps = "10_PlaceAndOrientContigs/overlaps.removed.la", + pruned = "10_PlaceAndOrientContigs/pruned.la" + message: "Removing overlaps" + threads: 1 + shell: + """ + cat {input.prunederr} > {output.pruned} + awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} + """ \ No newline at end of file diff --git a/scripts/LGcutoff.sh b/scripts/LGcutoff.sh index 5224c44..5f189c8 100755 --- a/scripts/LGcutoff.sh +++ b/scripts/LGcutoff.sh @@ -8,7 +8,7 @@ Essentially, set an LG maximum, and markers above it will be assigned to group 0 [usage]: LGcutoff.sh # unassign all markers in LG's 25+ -[example]: scripts/LGcutoff.sh 3_SeparateChromosomes/map.13 24 +[example]: LGcutoff.sh 3_SeparateChromosomes/map.13 24 EOF exit 1 diff --git a/scripts/extract_markers.sh b/scripts/extract_markers.sh index acafdaa..4848635 100755 --- a/scripts/extract_markers.sh +++ b/scripts/extract_markers.sh @@ -6,7 +6,7 @@ cat < -[example]: scripts/extract_markers.sh data.filtered.lepmap3.gz +[example]: extract_markers.sh data.filtered.lepmap3.gz EOF exit 1 diff --git a/scripts/iterate_js2all.sh b/scripts/iterate_js2all.sh index fee7cf3..fad7301 100755 --- a/scripts/iterate_js2all.sh +++ b/scripts/iterate_js2all.sh @@ -6,7 +6,7 @@ if [[ -z "$1" ]]; then cat < -[example]: scripts/iterate_js2all.sh 3_SeparateChromosomes/map.31 22 31 5 123 +[example]: iterate_js2all.sh 3_SeparateChromosomes/map.31 22 31 5 123 EOF exit 1 fi @@ -39,5 +39,5 @@ done echo "The generated maps are named LOD.LODlim.LODdiff.js2all" # generate a summary of the results -scripts/MapSummary.r JoinSingles2All_iter +MapSummary.r JoinSingles2All_iter diff --git a/scripts/lepmap2anchor b/scripts/lepmap2anchor index d666ab3..a5c46f3 100755 --- a/scripts/lepmap2anchor +++ b/scripts/lepmap2anchor @@ -6,7 +6,7 @@ cat < -[example]: scripts/lepmap2anchor snps.txt +[example]: lepmap2anchor snps.txt EOF exit 1 @@ -15,7 +15,7 @@ fi LG=$(ls 7_Intervals/*.intervals | wc -l) if [ ! -f $1 ]; then - echo "Error: marker file $(echo $1) not found. It may be generated using scripts/extract_markers.sh" + echo "Error: marker file $(echo $1) not found. It may be generated using extract_markers.sh" exit 1 fi diff --git a/scripts/refinemap.sh b/scripts/refinemap.sh index 961558f..d1fe0e4 100755 --- a/scripts/refinemap.sh +++ b/scripts/refinemap.sh @@ -9,7 +9,7 @@ Attempt to refine a map from SeparateChromosomes2 by splitting out markers from Uses 4 threads and distortionLod=1. Requires an input map, which linkage group to modify, LOD start:end values to iterate over, and a minimum size for new clusters. [usage]: refinemap.sh -[example]: scripts/refinemap.sh 3_SeparateChromosomes/map.31 1 22 70 30 +[example]: refinemap.sh 3_SeparateChromosomes/map.31 1 22 70 30 EOF exit 1 fi @@ -35,4 +35,4 @@ for i in $(seq $LODMIN $LODMAX); do done # generate a summary of the results -scripts/MapSummary.r $1.refine +MapSummary.r $1.refine diff --git a/scripts/usage b/scripts/usage index d7a1411..8e3daec 100755 --- a/scripts/usage +++ b/scripts/usage @@ -8,7 +8,7 @@ Should be used in main project directory. Module names are case-sensitive. [usage]: params -[example]: scripts/usage OrderMarkers2 +[example]: usage OrderMarkers2 LepMap3 modules: - ParentCall2 From 88d6ee7f313b400b58fcd72d47f1f766bc1ca9e4 Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Wed, 16 Feb 2022 16:35:09 -0500 Subject: [PATCH 43/46] rm old paths --- rules/LepAnchor/mask_and_chain.smk | 2 +- rules/LepAnchor/place_orient3.smk | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rules/LepAnchor/mask_and_chain.smk b/rules/LepAnchor/mask_and_chain.smk index eaa763c..9e06d0e 100644 --- a/rules/LepAnchor/mask_and_chain.smk +++ b/rules/LepAnchor/mask_and_chain.smk @@ -23,7 +23,7 @@ rule repeatmask: threads: 30 shell: """ - software/LepAnchor/deps/Red -gnm 8_RepeatMask/inputgenome -msk 8_RepeatMask -sco 8_RepeatMask -cnd 8_RepeatMask -rpt 8_RepeatMask > {log} 2>> {log} + Red -gnm 8_RepeatMask/inputgenome -msk 8_RepeatMask -sco 8_RepeatMask -cnd 8_RepeatMask -rpt 8_RepeatMask > {log} 2>> {log} echo "- Compressing repeat-masked genome from Red" gzip --stdout 8_RepeatMask/*.msk > {output} && rm 8_RepeatMask/*.msk """ diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index e72008d..bc89f6b 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -19,7 +19,7 @@ rule place_orient3: threads: 2 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f software/LepAnchor/scripts/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} + gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} """ rule prune_contigblocks: @@ -30,7 +30,7 @@ rule prune_contigblocks: message: "Pruning contig blocks without map support and removing overlaps" params: chrom = lg - shell: "awk -f software/LepAnchor/scripts/prune.awk {input} > {output.chrom} 2> {output.err}" + shell: "awk -f prune.awk {input} > {output.chrom} 2> {output.err}" rule prune_post: input: @@ -45,5 +45,5 @@ rule prune_post: shell: """ cat {input.prunederr} > {output.pruned} - awk -f software/LepAnchor/scripts/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} + awk -f removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} """ \ No newline at end of file From 5193af6de8415b50be7eb37fbed441a7cf42120e Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Wed, 16 Feb 2022 22:34:03 -0500 Subject: [PATCH 44/46] update script paths --- rules/LepAnchor/place_orient3.smk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index bc89f6b..90cd234 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -19,7 +19,7 @@ rule place_orient3: threads: 2 shell: """ - gunzip -fc {input.chain} | java -cp software/LepAnchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} + gunzip -fc {input.chain} | java -cp $CONDA_PREFIX/bin/lepanchor PlaceAndOrientContigs chromosome={params.chrom} numThreads={threads} $(awk -f $CONDA_PREFIX/bin/pickorientation.awk {input.chrom}) bed={input.bedfile} map={input.lift} chain=- paf={input.paf} proximity={input.prox} evaluateAnchoring={input.propogated} improveAnchoring=1 {params.datatype} {params.extras} > {output.chrom} 2> {output.errors} """ rule prune_contigblocks: @@ -45,5 +45,5 @@ rule prune_post: shell: """ cat {input.prunederr} > {output.pruned} - awk -f removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} + awk -f $CONDA_PREFIX/bin/removeOverlaps.awk {input.bedfile} {input.prunedchrom} > {output.overlaps} """ \ No newline at end of file From 589d2e538b82f1c61c54959c37d502c9cf3e4b9f Mon Sep 17 00:00:00 2001 From: Pavel Dimens Date: Wed, 16 Feb 2022 22:36:12 -0500 Subject: [PATCH 45/46] missed one --- rules/LepAnchor/place_orient3.smk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/LepAnchor/place_orient3.smk b/rules/LepAnchor/place_orient3.smk index 90cd234..342eeb1 100644 --- a/rules/LepAnchor/place_orient3.smk +++ b/rules/LepAnchor/place_orient3.smk @@ -30,7 +30,7 @@ rule prune_contigblocks: message: "Pruning contig blocks without map support and removing overlaps" params: chrom = lg - shell: "awk -f prune.awk {input} > {output.chrom} 2> {output.err}" + shell: "awk -f $CONDA_PREFIX/bin/prune.awk {input} > {output.chrom} 2> {output.err}" rule prune_post: input: From 5a32c3bca2745c17f9f42cbb2eed02b9a3954697 Mon Sep 17 00:00:00 2001 From: "Pavel V. Dimens" Date: Thu, 24 Feb 2022 11:02:31 -0500 Subject: [PATCH 46/46] Update build.sh --- .misc/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.misc/build.sh b/.misc/build.sh index 56ae559..4ff9a13 100644 --- a/.misc/build.sh +++ b/.misc/build.sh @@ -5,8 +5,8 @@ mkdir -p $PREFIX/bin mkdir -p $PREFIX/bin/lepmap3 mkdir -p $PREFIX/bin/lepanchor # LepWrap executable +chmod +x LepWrap cp LepWrap $PREFIX/bin/ -chmod +x $PREFIX/bin/LepWrap # associated scripts chmod +x scripts/* cp scripts/* $PREFIX/bin/