From 7ae4dd28ce3071c1fe8a55eee850fb634e45b4b5 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Tue, 23 Jun 2026 16:30:23 +0200 Subject: [PATCH] version from https://github.com/Davis-Garrad/NICOS-TNMR --- commands/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 158 bytes .../__pycache__/tnmr_commands.cpython-312.pyc | Bin 0 -> 14957 bytes commands/tnmr_commands.py | 303 ++++++++++++++++++ setups/frappy_scoutnmr.py | 43 +++ sinks/__pycache__/HDF5_NEXUS.cpython-312.pyc | Bin 0 -> 14103 bytes sinks/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 155 bytes 6 files changed, 346 insertions(+) create mode 100644 commands/__pycache__/__init__.cpython-312.pyc create mode 100644 commands/__pycache__/tnmr_commands.cpython-312.pyc create mode 100644 commands/tnmr_commands.py create mode 100644 setups/frappy_scoutnmr.py create mode 100644 sinks/__pycache__/HDF5_NEXUS.cpython-312.pyc create mode 100644 sinks/__pycache__/__init__.cpython-312.pyc diff --git a/commands/__pycache__/__init__.cpython-312.pyc b/commands/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19201de48dea08709752aa2c2a47dfdacc1defd7 GIT binary patch literal 158 zcmX@j%ge<81dXS7vq1D?5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!venPX&rQ|Oi7!sf zP0cIQ&&y2CFODzIEP%3#GxG}dOY(Ay^po>*a})DYiuL2;GxIV_;^XxSDsOSvwtF1ma>4<0CU8BV!RWkOcr3Zznqd literal 0 HcmV?d00001 diff --git a/commands/__pycache__/tnmr_commands.cpython-312.pyc b/commands/__pycache__/tnmr_commands.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24bf7a2ef136553859e0550630f4fbcba3af3a23 GIT binary patch literal 14957 zcmch8dvF^^dgshwKmr6v5ELoC#gQmVghWv<>S@U~De7%e*4v`2zz}9gf&u|*29!h? zTyrM$|63W4cy#@!e+Y>Z&N) zSKc`H$Njz@3;;r0t=E5~i|Fb3`s<#4eEs$J;!lc;i#Vh|duwNZ_hyd!M|!c7GhO(c zb8y@(PUd7j%5_^$zMIFp5ETZ5Zh^O8j&28Fj;J``>~=Dk7%dnm>@H+wXVf*|?sg9p zbr+@aJpA0BczP37#7rf%-@6?nbHDIA^AZb9a8E`>L_Zc%n1cgiC2 zf?;Q%z;q{K18Pvi(2_UWqgO)1niL8rkl{}X$72cQYC^)WXi8E~T$K`iiiFvRY+$Q& zG!`9Z;6!{-iYk|tsFdkcieKng!U^hg2y+@{U8?b@#u~&2l~^R!D`D1BI2zZe9Pg2O zB2h(amBRLHXlit(!GpNZ%aDN+@hL8dl9h(JfY7xME91ft*a%P-H6_}kQ6JJ*9qaqz z1Il_@(E%lPd3`Jrj%&dyk->BcU%s@S2E9JL2^xC~+Hb9c!=@`3jKm^|U@%#hnO>_6 z3*a-F8`&r~TU9?Mey{j*8rtGh7)8x3E@Ucc?a~OJ;``}? zdC5KqBSMM~S<7TI@tiN7panaL?XNu9b>w((&#B|b_ja9p>1^=Cp7yS;gI)Vo8irY< zTpd(Yka;X&inQsb`-0LNi4i5MeqaO~D(#0U$OHIY&}fGwNm7>(&>ak-p`v2NmgKHj z8rT9HZ3nVZ?u!D>+o*fieCn^7@i!X&#u`c~<#RcZxVhjSNF8QP3vPz z%{IdkjrW>jB-RrTh}NpRl&j%jVt7z7-Bh|9iVmTeP(xwmLMVJuqh{ER^mc%Lm8(zF zuLauoMZGGh3FuQ6EHek-XO~roJ|)uImynu6twXH<%1Sf@h6S>=vHHNErSNb#3W%0aTO>(o?QLz5R&08BO38m*P@3LTABvifHloD=|d{x3)N5ObO$* z2&u!890@0IYfzEcYDktDCrl4H5oZh%hcgY(luJWOEUdIhsJfs?J&H^vI=<57NQhyt z?v1YoF|k{um3@5g)zAPK;?5%WHW=KveS@@H>P-(R$d;#h3+5}eZD^4;qOf{{v;kAz zv^tP(vSWkvbdyc3Xhco6;K!PvS-J`1C`XQVo_c9-u&e#Z-d20)L3VBImd;6RAlRMY z4m7t$b)J3hskWHSAZ~*oZit{d6l=Cd6%b7i8#`DyHrw=CFV;SrMQnqEw8225y$!Ox zRo7s<0!~vHyn+l`lysFTp@J}s$axUt3o!)&DU@>byLUhs`~Mh zhU@fb`(uxH^Z=v8u~Yf7Xbgshy7FP|(1 zc0sBz#b4$${(3{oiI%KgO4xDnYJ3kz7nZ*}~5bd)7 zA&;09Bnt2WMKfL4F2*@c5o@cN4iyq3e;EReGB8N3Og^H~ox`X!L#SmLLbW^A)V;tu zj-U1)kbziT1Px0uUe=PQo@v}=R#$0p$rQg>kc28FZe&$~@ z);{YkpQxX_WK;)cs#}ff*6HdEdeuh5yK(HmV_)g`k%zwJv(15V*M$G3XX3!a(x!Pg zSGIP(h;w_#U1LeZRejAd@8e3!=F2#*Z}iZw=Nm<5-Mrv%*Uc_ltJkf&cI4Od0`IPy zE2|wl`qdLBS6uUz)`+2g$J1WV-LDtgH#+W1j`o$p{YIY3D_!j^&igG6l#?%NiIAGe z<=qMnR1PbT4Gmm?pb8QlB-vY&nh7Bi3rB~v$YmuECK9z1ttTVdWgs?43mJlibSc0vQZElaq+1ut_XehXgaAS9usryNNfmXsh1^p=FYk;D{k z3fH?t9IFvWO2lZL>CxiS9C7CKP6sqErJVNhLLb#4<~ZU!ft;t3 zS0ipgO+AY0i}-0*kwGGKab5fgUO#bCKYixEotyS`e{}8v|NZh?RX3|9mcLc|XX0PD z-giy;|J3tC`9Jo3(0Aw3hZhZL$K8!a!>+rbd;DL8jfReCf5(G!qX%?f_s>_XnfA22 z-#l^b`pFxwTzh3a^u2SF&4#B%7hC@MGY40Cj{j%V)1rCt@f+>_4ySm3Rb5Asm^@AD z4!dL{G0+|^;PsayD(Ny1BQE3U67R-Qj!LAo4JhF3n$)Al2f&>dxd;>uv(&4`dC#l?iyERkW$A$TlCh#(P=3gdzT!&zkepTeO7k(Xpfa_N3}aA915 z;NU_^lttpgd3}hoGaq`r2Aq{f36jlENoWtyR0N1j1zo?iYk&E@|MQrkNWh@`eKTF#dcR@yt6;pjmy``$!fkTySP(S~}SMvb5LC&-8u zbDomXJ#(J28Bd+zshc=4?O8tBIV%>9was`W!y`?$8J^XKxO#L?8o1K%tei|3p0$R! zHUn%jJWW#~sMio%M)!PNTnbk1@{C;?ue4q4L6zYXQerNl+ERNb7vpb%gdS!O^NTV|XGFyhQo zf0N_!11zF-JU~t$JVt1_R<{`1^(DlbPH-Ox*8T-}c4TqCokd@!i{xr1e7O_xL?{|0p1}9#?N4S@%!;-c z8|Xo2v_;xlWNe@O<)a5bF0P<+>wi?S<8I^KOVbtYhO7OWd)~p7EPd=L*F{UKPQLZ8 zc_!`U{#V`r^#WD~tN$83M3pRiYM3&aG6h-QqLn0Qrw`MB6q9lx%hF!3w3tCOE6a`)ydla7|%sYOCZRS5~#KzGx1f^9!?<=Y-k|v3c+nIFG{)8$Wc1C} zc2gMAdjIHWtNzK;>VKX=m4EmLKx5ihg4AsC0hlNpP(UC;zzm@he=>_nG@w@qk^_T* zLN!8A*j0x$)7cjvQZ=)1AQBsbt;j3@tAQoTEQI<(O-v68u%9regI0)!mMon%SQJ^^ zsxTp`DOA3XpSB$o%Oq*9Z|us%HpACA?O8e6Ip_6_7f!U@^i6xE(YRR>M^;4I0|3(XC(|7A;%WKDXOtilfo?Lx9I(6pG@ejI$D40bG&OJ%Be4PdfA^D-0{E326)Yc|T7rz)q*HjW;i6^q7&@#n_ZODQ|-{?rwnFN7Qq9Xap&KNUAH!d*a{$ zJZy_8mVhpzN&>ow@(3vIm;3}M>DhS+P_X6v1ZZhK3>FZsABtmsg8ki4Q2G%rS48uJ{EF+M9BTiRe^MYJM=IvJ_dwa}uAmz`OMN9!XKd3kMYw;#IYP^ zOBN`Rk2i0f*}UJ_ynmJooyO))Ryb^IKFkU)8Jk~Xg?+~6eJK2B*I(|+K4vx(_=(?L zU89Q?^W~hkUKi^uCinuGKvbm%HfxwaEORn6LxR!>CCkgRxI#w)^a7I`kpm;fKgMOg z&`SJU$kCC#{%yfv3t1w>e!30wl8rIqglonTq*KpQf*3VRS(`L0ys#Cgp|JegDgrFi zHh59e?1zGppJYBku1}Uy1vXOzgUn^nj(+l6<|R1?Ll#B~6Xf$^seh?m%04smbEOK$ zd2JNd72@=HDTC!keQjPPUhR78nvlhdHMlGa6XcO&39Xjd^#@;boJC&` zFI7Z_P1$)77oXaP?@QV5+9g0&*yZf=;x@E6njO2GS$&N2nX7~S|5|IkU~DzN^Vs<7 zMHR*Gn6l@}#`XYvIM^8pyQEDj&ruSITW&(metz zv#U8>X66c3_OQH|<&OR}c2C)7x`s?`c`02(mW;gABgjQyy(KB3e`8)1UTrGFoIv-l zVrNHuBc<@MxCAa$YS*Io64{gTCDDS-w37TW{n*v~l*$BU5d~_(IDdT=X>+hJ<8sc4{AlRz`fz`Z+p7lo()dCXzwM@!C&+(%U{&UZR9lm z3JmOL;aQ?_*zm+oySJ5`x@)9NcF0_G*@*vlA0t-vnPbZxm&}cneXDF3YeUZ~{FU^0 z0wrBZ4~t%s_MU9tDebe2{|_+6UlB{_wFdk61-QBt@>e-w*6xMzKM`;x{aU0q7K-j< z_ZO>9)`fIc{0LU2*hp3kHBI+li#$)uhK3TsfJ`{yDGERX>upO&P*3c=vmR$Q9 zbID&(VNym!5@f)*)TO*K1j{~r(XG-+JHle8R1|2)5es}+7PE+j$gssTi|siu-!~H= zEu*jmOeTUdbBq55yAGiFyL zB2u5Dj06U=Fug?RgRP9|vKIs!2G1fC&MP66La-2S5?A4wK!`}#bPoFa?L#tMO9Qk5z8TvUdU z55>Z4wCZ8>sL}0gsR|47v`fmi_pwL|l@7B>js5~W3ue25EzY(%-_jYMWcZ}XCDXp9 zF>$VV>Gg9HEKaHZX8l_YV;zr|uN>=qM2TP_Kj_BUH1<^AT zum5nTzIEU9+Wq>P1A6_zN8UrT-qIPbWO${?ibvknk1OlO4#0K$p8IY0ZO_=j|FLx0 zY-R1O;hV#6y*@5Ju9Mz7{PyA7N2ZoTg}CD|>NbzN=1OZO+Gc87jM|n*rR#paq;{^l z{=MqAt8dqicg)o`ytm`+9k-vGDl=-=PF;8iog*=`?Xa=!@PovEANgqH;kH-js#Z*H ze#bX!ee@mA7fueJd1kBWZLh-pyE>qkHqKQqdqo^;uG?_O|G{a!eDxIn zuzU^dnl%BaEDfzw;pt_Y#yyX>x8G}fP)Xx{!gKnz zqYUxycirEmZ+qD)KX*S1-_1IM7~5Dj?t>KDxNBxbyRoAEXDjyHJT%@pv2tDzE9z%o zJor)DPqykW9G~z_mE1`f&Cflo+6C80Vw4s^`!d9HCDmi+em#Gh7b@!Ksv6$&yzQB(T5D9T)mslg=zOqMKQ=PYac}S^1ok)} zd}+Nsu^yk(;}q3>i$6_|b!UVxIIj8?;pbI#UwwJb>i=K9xWIGXiqAN~+4ixkYR=Xg zYYkVeUbkW5;7r{Hqi(}=-KH7WX2Z4lU*?^_{7R$4_??DBPVW7^OAqdF{O$IR0zCfy z1>b?^#Rnx7C_iW_rF@GENDrRhdT?vuM{Q0(KHA|tBou!1yaVtb3oLiK4;MLpT(aq~ z)A19h6XoRlUt1ZlLq%AeL&mVr%=mQ7r1)h8fmWeRNJ%rrsUX6HF%5*o41tp~>@bD7 zG{@o+f@Oz@f7|gg;5b$g6paZy2SHI_&kNKI66~3}QG(DlkXo$oSwFstF0YOjxqVwFLr0BBOMqG6Dt!tM5aof|Vv! zKx`8td`6*JIObG=%++MXAX^X^aeads2Lhac-A(J+7*O;|2u!wOd;+ZjFO!!n!KVi! z$}{sZ8OsVO-HBS$*%OV264w3h2wl+7{w%?>(i$-@b&`7ebIM+#+S2rhg7%1*6vBMB zFw?_aj|rQCl2A7hwv)|>1(f7U8kV3@Lbw^bqNe@`Ey`gz);>T6>rN3@RC1>J;~+ zj&{t!vwvjTRWsWDY2~)Nj=SflD-XjrIJdygSlRN!ZhiZK>2(M7mP2~Q;YZ?2pSsJw zSBv1T@k>9bLReQ>!z6E%tr*)gw^Vwxy!~F|y%GJ?Y5mk0eg7-d%g;_P?H=2|KswPI z){{l@FC9}uciQiS?wru;cj!uw-rM(3>AS1`*QN*je`$WO@&1|zC+@fE$4=_)r}R^= z8M{MzY|yB`^vJ6&=x*ZmsclmMy}s>{cgvi&dZKYc(^u~PsrUIO9{@+yF$<_nvBw>Y40jMu*2}~xVzWzw!?R^a1GY}@X#Ig!!CX8b9bM+ zH}v4dy^H#>e*NO0en~Yhs=9Vr@4tfU_A1{__icyp5?#JWgyZx$A)FL&KOeyZ_j8B9 z?&o%a-Os0m3xu*XEc{=NbIQV()fntp?N=Ic+55{o8u#&zzvG?ziVBlQ=qgGF23_fk zYdLy_CTZ~T$WaR39U3H84)b_tEr3M*)SOARERbk%H+L7lilAe@AGDI6Y*vLU0R~d0 zYa@^=>t+KxL|{V>J1iHWgQ}!u2L$zx@gOfK^o@jSJHZ_op~+uVJ5ZVDgsrmXk&bo= zW(ThRHIUa4L6e?9qBB>zP>K7hg@jU`g90xv#d0My z3eHp!GM@z$u%c#`*yt>nT2slmt&$~O1*^~L&8luiY-c2_g70(cA5ne9!se~%cpx%+ zn;xrfC=rQjDv^*C^iS743p8c{oGc#BQWamL3dYsQ_G|Gn@ec9qQOtbxrd}XSZ(ujN8h$5z@ +# +# ***************************************************************************** + +import time +import math +from datetime import datetime +import traceback + +from nicos import session +from nicos.core.data import DataManager +from nicos.core.data.dataset import PointDataset, ScanDataset +import nicos.core.constants as consts +from nicos.commands import helparglist, usercommand, parallel_safe +from nicos.commands.basic import sleep as nicossleep +from nicos.commands.device import maw +from nicos.utils import createThread + +TNMR_CURRENTLY_SCANNING = None + +class tnmr_scan: + '''Always acts as a context manager for the data manager. Only the top level tnmr_scan object actually controls the opening and closing of files. ''' + def __init__(self): + self.toplevel = False + + def __enter__(self): + global TNMR_CURRENTLY_SCANNING + + if(TNMR_CURRENTLY_SCANNING is None): + dm = DataManager() + db = dm.beginScan() + TNMR_CURRENTLY_SCANNING = dm + self.toplevel = True + + return TNMR_CURRENTLY_SCANNING + + def __exit__(self, exc_type, exc_value, traceback): + global TNMR_CURRENTLY_SCANNING + if not(TNMR_CURRENTLY_SCANNING is None) and (self.toplevel): + TNMR_CURRENTLY_SCANNING.finishScan() + TNMR_CURRENTLY_SCANNING = None + session.log.info('Closing scan file context') + return False + +@usercommand +@parallel_safe +@helparglist('pulse width (us), pulse height (a.u.), delay time (us), phase cycle (str, e.g., "0 1 2 3")') +def generate_pulse(pw, ph, dt, pc): + """ + Generates the necessary dictionary for adding a pulse to the NMR pulse sequence, to be fed to TNMR via the 'go' command. + Example: + + p_180 = generate_pulse(5, 40, 1, '0 0 2 2') + p_90 = generate_pulse(2.5, 40, 50, '0 0 0 0') + TNMR_MODULE_NAME.sequence_data = [ p_180, p_90, p_180 ] + TNMR_MODULE_NAME.compile_and_run() + """ + d = { 'pulse_width': pw, 'pulse_height': ph, 'delay_time': dt, 'phase_cycle': pc } + + return d + + +@usercommand +@parallel_safe +@helparglist('a pulse sequence to be copied and altered, pulse(s) to be scanned (zero-indexed) (in the case of multiple, all will scan concurrently), variable name (i.e., "pulse_width", "pulse_height", "delay_time", or "phase_cycle"), list of values') +def generate_sequences(base_sequence, pulse_indices, var_name, vals): + try: + pulse_indices = list(pulse_indices) # convert to list if tuple, single element, etc.. This is so we can just treat it homogeneously throughout the function + except: + try: + pulse_indices = [ pulse_indices ] + except: + raise TypeError + + ret = [] + + for v in vals: + temp_seq = [] + for i in range(len(base_sequence)): # per-pulse + temp_seq += [base_sequence[i].copy()] + + if(i in pulse_indices): + temp_seq[i][var_name] = v + ret += [temp_seq] + + return ret + +@usercommand +@parallel_safe +@helparglist('start time (us), end time (us), number of points (end inclusive)') +def log_durations(s, e, N): + equidist = [(math.log(s) + (math.log(e) - math.log(s))*i/(N-1)) for i in range(0, N) ] + return [ math.exp(a) for a in equidist ] + +@usercommand +@parallel_safe +@helparglist('the TNMR instance/virtual device to pull parameters from, a pulse sequence to estimate') +def estimate_sequence_length_from_device(dev, seq): + eta = 0.0 # seconds + tnmr = session.getDevice(dev) + eta += tnmr.acquisition_time * 1e-6 + eta += tnmr.pre_acquisition_time * 1e-6 + eta += tnmr.post_acquisition_time * 1e-3 + for i in seq: + eta += i['delay_time'] * 1e-6 + eta += i['pulse_width'] * 1e-6 + eta *= tnmr.num_acqs + return eta + +@usercommand +@parallel_safe +@helparglist('a dictionary of parameters (acquisition_time, pre_acquisition_time, post_acquisition_time, and num_acqs), a pulse sequence to estimate') +def estimate_sequence_length(params, seq): + eta = 0.0 # seconds + eta += params['acquisition_time'] * 1e-6 + eta += params['pre_acquisition_time'] * 1e-6 + eta += params['post_acquisition_time'] * 1e-3 + for i in seq: + eta += i['delay_time'] * 1e-6 + eta += i['pulse_width'] * 1e-6 + eta *= params['num_acqs'] + return eta + +@usercommand +@parallel_safe +@helparglist('the TNMR instance/virtual device to be used, a sequence of pulse sequences to estimate') +def estimate_scan_length_from_device(dev, scan_seq): + total_eta = 0.0 + for j in scan_seq: + total_eta += estimate_sequence_length_from_device(dev, j) + return total_eta + +@usercommand +@parallel_safe +@helparglist('a dictionary of parameters (acquisition_time, pre_acquisition_time, post_acquisition_time, and num_acqs), a sequence of pulse sequences to estimate') +def estimate_scan_length(params, scan_seq): + total_eta = 0.0 + for j in scan_seq: + total_eta += estimate_sequence_length(params, j) + return total_eta + +@usercommand +@parallel_safe +@helparglist('seconds') +def timestring(seconds): + days = int(seconds // (24*3600)) + hours = int(seconds // 3600) + minutes = int(seconds // 60) + + if(seconds > 24*3600): + etastr = f'{days}d{hours-(24*3600)*days}h' + elif(seconds > 3600): + etastr = f'{hours}h{minutes - hours*60}m' + elif(seconds > 60): + etastr = f'{minutes}m{int(seconds) - minutes*60}s' + elif(seconds > 1): + etastr = f'{seconds:.1f}s' + elif(seconds > 1e-3): + etastr = f'{seconds*1e3:.1f}ms' + elif(seconds > 1e-6): + etastr = f'{seconds*1e6:.1f}us' + elif(seconds > 1e-9): + etastr = f'{seconds*1e9:.1f}ns' + + endtime = time.time() + seconds + enddatetime = datetime.fromtimestamp(endtime) + etastr += f' ({enddatetime})' + + return etastr + +@usercommand +@parallel_safe +@helparglist('a pulse sequence to display') +def print_sequence(seq): + session.log.info('------------------------') + session.log.info('PW |PH |DT |PC') + for i in seq: + delay_time = i['delay_time'] + + session.log.info(f'{i["pulse_width"]:<8}|{i["pulse_height"]:<8}|{delay_time:<8}|{i["phase_cycle"]}') + session.log.info('------------------------') + +@usercommand +@parallel_safe +def get_tnmr_params(dev): + tnmr = session.getDevice(dev) + params_dictionary = { + 'acquisition_time': dev.acquisition_time, + 'ringdown_time': dev.ringdown_time, + 'pre_acquisition_time': dev.pre_acquisition_time, + 'post_acquisition_time': dev.post_acquisition_time, + 'acq_phase_cycle': dev.acq_phase_cycle, + 'obs_freq': dev.obs_freq, + 'num_acqs': dev.num_acqs, + 'actual_num_acqs': dev.num_acqs_actual, + } + return params_dictionary + +@usercommand +@helparglist('the reference name of the tnmr module, a pulse sequence to scan') +def scan_sequence(dev, seq, additional_saving_lambdas={}): + try: + tnmr = session.getDevice(dev) + with tnmr_scan() as dm: # open a file if one is not already opened; if one is, this just gives us a reference to the appropriate datamanager. + pb = dm.beginPoint() + + tnmr.sequence_data = seq + print_sequence(seq) + estimated_length = estimate_sequence_length_from_device(dev, seq) + session.log.info(f'Point ETA: {timestring(estimated_length)}') + tnmr.compile_and_run(False) + + finished = False + starttime = time.time() # To signal when measurement started (approximately. This will not be nanosecond-precise, but it's good enough for our purposes) + + while not(finished): + data = tnmr.read() # get latest data, with records about the # of acquisitions that have been performed. + + finished = (tnmr.status()[0] <= 200) # at the start so final values will be written + nicossleep(tnmr.pollinterval) # A metric as good as any + if(finished): + st = time.time() # Start a timeout clock, essentially + while (time.time() - st < 30) and (tnmr.num_acqs_actual != tnmr.num_acqs): + nicossleep(0.5) # get up to date values + tnmr.read() + + # Now we need to put our sequence (list of dictionaries) into the form that our file handler wants (dictionary of dictionaries). The keys on the sequences should be informative of the order, so we just set them as the indices of the original list because that will never be unclear. + sequence_dictionary = {} + for i in range(len(seq)): + sequence_dictionary[i] = seq[i] + + # Construct a whole dictionary for all the different values we want to pass to the file writer. The key is going to be the key of the data in the end; in the NeXus handler, I've programmed in some "magic" identifiers, such as signal:, axes:, auxiliary_signal:, metadata/, and environment/. These each designate a different place for the data to reside ('/') or be given a NeXus attribute (':').and/or jhavereside and + params_dict = get_tnmr_params(dev) + full_value_dict = { + 'signal:tnmr_reals': (starttime, data['reals']), + 'auxiliary_signals:tnmr_imags': (starttime, data['imags']), + 'axes:tnmr_times': (starttime, data['t']), + 'tnmr_sequence': (starttime, sequence_dictionary), + 'tnmr_params': (starttime, params_dict), + 'metadata/nucleus': (starttime, tnmr.nucleus), + 'metadata/sample': (starttime, tnmr.sample), + 'metadata/comments': (starttime, tnmr.comments), + } + for fkey, func in additional_saving_lambdas.items(): + try: + full_value_dict['environment/'+fkey] = (starttime, func()) + except: + session.log.warning(f'Could not acquire parameter `{fkey}` for writing into NeXus file. Traceback: \n{traceback.format_exc()}') + pass + + dm.putValues(full_value_dict) + dm.finishPoint() + except Exception as e: + import traceback + session.log.warning(traceback.format_exc()) + +@usercommand +@helparglist('the reference name of the tnmr module, a list of pulse sequences to scan over, a list of (name, lambda) tuples to call (no argument) to be added to the save file') +def scan_sequences(dev, sequence_list, additional_saving_lambdas={}): + global TNMR_CURRENTLY_SCANNING + + st = time.time() + N = len(sequence_list) + + initial_estimate = estimate_scan_length_from_device(dev, sequence_list) + session.log.info(f'Beginning scan. ETA: {timestring(initial_estimate)}') + + with tnmr_scan(): # Make sure that everything is put together in one file! + for i in range(N): + seq = sequence_list[i] + etascan = timestring(estimate_scan_length_from_device(dev, sequence_list[i:])) + session.log.info(f'Beginning point {i+1}/{N}' + (f' ({float(i)/(N-1)*100:.0f}% complete)' if N>1 else '')) + session.log.info(f'Scan ETA: {etascan}') + scan_sequence(dev, seq, additional_saving_lambdas) + + et = time.time() + dt = et - st + + error = abs(dt - initial_estimate) / initial_estimate * 100.0 + session.log.info(f'Finished scan. Took {timestring(dt)} (error of {error:.0f}% from initial estimate, {error/N:.0f}%/point, {timestring(error/N)} per point).') + +@usercommand +@helparglist('the device whose parameters shoudl be updated, a dictionary of the parameters') +def update_device_parameters(dev, dic): + for k,v in dic.items(): + setattr(dev, k, v) diff --git a/setups/frappy_scoutnmr.py b/setups/frappy_scoutnmr.py new file mode 100644 index 0000000..5defe1d --- /dev/null +++ b/setups/frappy_scoutnmr.py @@ -0,0 +1,43 @@ +# ***************************************************************************** +# +# 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 2 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, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Module authors: +# Davis V. Garrad +# +# ***************************************************************************** + +from os import environ +description = 'frappy tnmr setup' +group = 'optional' + +sysconfig = dict( + datasinks = [ 'hdf5filesink', ], +) + +modules = [ "nicos_sinq.frappy_sinq.devices", "nicos_sinq.tnmr.commands.tnmr_commands" ] + +devices = { + 'secop_connection': + device('nicos_sinq.frappy_sinq.devices.FrappyNode', + description='TNMR PC SEC node', unit='', async_only=True, + prefix='nmr_', auto_create=True, service='main', + uri='tcp://129.129.156.124:5000', + ), + 'hdf5filesink': + device('nicos_sinq.tnmr.sinks.HDF5_NEXUS.HDF5ScanfileSink', + filenametemplate=['file_%(proposal)s_%(month)02d-%(day)02d-%(hour)02d-%(minute)02d-%(second)02d.hdf'], + ), +} diff --git a/sinks/__pycache__/HDF5_NEXUS.cpython-312.pyc b/sinks/__pycache__/HDF5_NEXUS.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5c92db7fbf9b4aa07465cdac4f048dedb44cd5e GIT binary patch literal 14103 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 literal 0 HcmV?d00001 diff --git a/sinks/__pycache__/__init__.cpython-312.pyc b/sinks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb2fd62551311a2aedbb2c28885a89760b15d4ef GIT binary patch literal 155 zcmX@j%ge<81dmSfW`XF(AOanHW&w&!XQ*V*Wb|9fP{ah}eFmxdWu>2ypPQu8W#rpB_nR%Hd@$q^EmA5!-a`RJ4b5iY! WSb;_{0&y{j@sXL4k+Fyw$N~VxZYFO4 literal 0 HcmV?d00001