add files needed for nicos

This commit is contained in:
2026-06-23 16:49:03 +02:00
parent 542bbc0c13
commit ba9ddfdd0b
22 changed files with 971 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
__pycache__
Binary file not shown.
View File
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,59 @@
# *****************************************************************************
#
# 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 <davis.last@psi.ch>
#
# *****************************************************************************
# do "accessory" stuff. Set field, temperature, etc.
SetEnvironment(tt, mf) # Add the PPMS temperature and field to the file at every write.
#maw(tt, 3) # set PPMS temperature
#maw(mf, 3) # set PPMS field
#nicossleep(20*60) # 20 minutes
# ...
# Create the pulse sequence
# generate_pulse(pulse_width, amplitude, delay_time, pulse_cycle)
pw90 = 2.3 # us
amp = 40 # percent
tau = 50 # us
p180 = generate_pulse(2*pw90, amp, 1, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3') # 180deg
p90 = generate_pulse(pw90, amp, tau, '0 2 0 2 1 3 1 3 0 2 0 2 1 3 1 3') # 90deg
p180_2 = generate_pulse(2*pw90, amp, 0, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3') # 180deg
seq = [ p180, p90, p180_2 ]
# Create the list of sequences to scan (specifically, for a T1 scan)
delay_times = log_durations(10, 1_000_000, 20)
# generates a list of sequences; copies of seq are made, only the zeroth pulse is modified. Each copy is given a 'delay_time' value from delay_times
seq_list = generate_sequences(seq, [0], 'delay_time', delay_times)
# Set some parameters independent of pulse sequence
globalparams = {
'acq_phase_cycle': '0 2 0 2 1 3 1 3 2 0 2 0 3 1 3 1',
'acquisition_time': 204.8, # us
'num_acqs': 100, # "1D scans" in TNMR. Our 16-fold phase cycling means this should be a multiple of 16 for proper averaging
'ringdown_time': 15, # us
'post_acquisition_time': 250, # ms
'obs_freq': 45.5, # MHz. Receiver frequency
'nucleus': 'NUCMgReS',
'comments': 'An example of a T1 scan',
}
update_device_parameters(nmr_daq_scout, globalparams)
# Acquire data
scan_sequences(nmr_daq_scout, seq_list) # gather the data
@@ -0,0 +1,62 @@
# *****************************************************************************
#
# 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 <davis.last@psi.ch>
#
# *****************************************************************************
# do "accessory" stuff. Set field, temperature, etc.
SetEnvironment(tt, mf) # Add the PPMS temperature and field to the file at every write.
#maw(se_tt, 3) # set PPMS temperature
#nicossleep(20*60) # 20 minutes
# ...
# Create the pulse sequence
# generate_pulse(pulse_width, amplitude, delay_time, pulse_cycle)
pw90 = 2.5 # us
amp = 40 # percent
tau = 50 # us
p180 = generate_pulse(2*pw90, amp, 1, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3') # 180deg
p90 = generate_pulse(pw90, amp, tau, '0 2 0 2 1 3 1 3 0 2 0 2 1 3 1 3') # 90deg
p180_2 = generate_pulse(2*pw90, amp, 0, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3') # 180deg
seq = [ p180, p90, p180_2 ]
# Create the list of sequences to scan (specifically, for a T1 scan)
delay_times = log_durations(10, 1_000_000, 20)
# generates a list of sequences; copies of seq are made, only the zeroth pulse is modified. Each copy is given a 'delay_time' value from delay_times
seq_list = generate_sequences(seq, [0], 'delay_time', delay_times)
# Now, the reader should note how easy manipulating pulse sequences really is
for i in range(len(seq_list)):
seq_list[i][1]['delay_time'] = max(seq_list[i][0]['delay_time'] - 10.0, 0.1)
# Set some parameters independent of pulse sequence
globalparams = {
'acq_phase_cycle': '0 2 0 2 1 3 1 3 2 0 2 0 3 1 3 1',
'acquisition_time': 204.8, # us
'num_scans': 1024, # "1D scans" in TNMR. Our 16-fold phase cycling means this should be a multiple of 16 for proper averaging
'ringdown_time': 1, # us
'post_acquisition_time': 250, # ms
'obs_freq': 45.5, # MHz. Receiver frequency
'nucleus': 'NUCMgReS',
'comments': 'An example of a T2 scan',
}
update_device_parameters(nmr_daq_scout, globalparams)
# Acquire data
scan_sequences(nmr_daq_scout, seq_list) # gather the data
@@ -0,0 +1,50 @@
# *****************************************************************************
#
# 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 <davis.last@psi.ch>
#
# *****************************************************************************
p90 = generate_pulse(2.5, 40, 50, '0 2 0 2 1 3 1 3 0 2 0 2 1 3 1 3')
p180_2 = generate_pulse(5, 40, 0.1, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3')
seq = [ p90, p180_2 ]
# Set some parameters independent of pulse sequence
globalparams = {
'acq_phase_cycle': '0 2 0 2 1 3 1 3 2 0 2 0 3 1 3 1',
'acquisition_time': 204.8, # us
'num_scans': 1024, # "1D scans" in TNMR. Our 16-fold phase cycling means this should be a multiple of 16 for proper averaging
'ringdown_time': 15, # us
'post_acquisition_time': 250, # ms
'obs_freq': 41.59, # MHz. Receiver frequency
'nucleus': 'NUCMgReS',
'comments': 'An example of a field sweep',
}
update_device_parameters(nmr_daq_scout, globalparams)
fields = [ 6.8 + i*1e-3 for i in range(2000) ]
print(timestring(estimate_scan_length(globalparams, seq)*len(fields)))
with tnmr_scan(): # Enters a mode of manual file control. Values will now be written into a single file until the context is lost
for field in fields:
maw(se_mf, field) # assuming se_mf controls the reader's external field strength (PPMS, etc.)
print_sequence(seq)
# Acquire data
scan_sequence(nmr_daq_scout, seq) # gather the data
+65
View File
@@ -0,0 +1,65 @@
# *****************************************************************************
#
# 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 <davis.last@psi.ch>
# Tina Arh <tina.arh@psi.ch>
#
# *****************************************************************************
# Select the parameters that are written under environment in HDF file at every write
SetEnvironment(tt, mf, nmr_TSSOP16, nmr_RP100Node_CH1, nmr_RP100Node_CH2, r1, tps)#, ZVLNode)
# PPMS: Set field & temperature
#maw(tt, 3) # set PPMS temperature (in Kelvin)
#maw(mf, 3) # set the starting PPMS field (in Tesla)
#nicossleep(20*60) # 20 minutes
# ...
# Create the pulse sequence
pw90 = 2.5 # us
amp = 25 # percent
tau = 20 # us
# generate_pulse(pulse_width, amplitude, delay_time, pulse_cycle)
p180 = generate_pulse(2*pw90, amp, 1, '0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0') # 180deg
p90 = generate_pulse(pw90, amp, tau, '0 2 0 2 1 3 1 3 0 2 0 2 1 3 1 3') # 90deg
p180_2 = generate_pulse(2*pw90, amp, 0, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3') # 180deg
seq = [ p180, p90, p180_2 ]
# Create the list of sequences to scan (specifically, for a T1 scan)
delay_times = log_durations(1, 600000, 20)
# generates a list of sequences; copies of seq are made, only the zeroth pulse is modified. Each copy is given a 'delay_time' value from delay_times
seq_list = generate_sequences(seq, [0], 'delay_time', delay_times)
# Set some parameters independent of pulse sequence
globalparams = {
'acq_phase_cycle': '0 2 0 2 1 3 1 3 2 0 2 0 3 1 3 1',
'acquisition_time': 204.8, # us
'num_acqs': 16000, # "1D scans" in TNMR. Our 16-fold phase cycling means this should be a multiple of 16 for proper averaging
'ringdown_time': 11, # us
'post_acquisition_time': 200, # ms
'obs_freq': 42.09, # MHz. Receiver frequency
'nucleus': 'NUCMgReS', # For example, 139La. Will be used in a filename!
'comments': 'An example of a T1 scan',
'title': 'T1', # Goes at the beginning of a filename!
}
update_device_parameters(nmr_daq_scout, globalparams)
# Acquire data
scan_sequences(nmr_daq_scout, seq_list) # gather the data
+75
View File
@@ -0,0 +1,75 @@
# *****************************************************************************
#
# 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 <davis.last@psi.ch>
# Tina Arh <tina.arh@psi.ch>
#
# *****************************************************************************
# Select the parameters that are written under environment in HDF file at every write
SetEnvironment(tt, mf, nmr_TSSOP16, nmr_RP100Node_CH1, nmr_RP100Node_CH2, r1, tps)#, ZVLNode)
# PPMS: Set field & temperature
#maw(tt, 3) # set PPMS temperature (in Kelvin)
#maw(mf, 3) # set the starting PPMS field (in Tesla)
#nicossleep(20*60) # 20 minutes
# ...
# Create the pulse sequence
pw90 = 2.5 # us
amp = 40 # percent
tau = 50 # us
# generate_pulse(pulse_width, amplitude, delay_time, pulse_cycle)
p90 = generate_pulse(pw90, amp, tau, '0 0 2 2 1 1 3 3 0 0 2 2 1 1 3 3')
p180_2 = generate_pulse(2*pw90, amp, 0, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3')
seq = [ p90, p180_2 ]
# Create the list of sequences to scan (for a T2 scan, the delay after the first pulse - tau - increases linearly from tau_min to tau_max)
tau_min = 50 # us
tau_max = 20000 # us
n_points = 20
delay_times = [tau_min + i * (tau_max - tau_min) / (n_points - 1) for i in range(n_points)]
# Generates a list of sequences; copies of seq are made, only the zeroth pulse is modified. Each copy is given a 'delay_time' value from delay_times
seq_list = generate_sequences(seq, [0], 'delay_time', delay_times)
# We now modify the delay after the second pulse to be a fixed time shorter than tau, this means the spin echo will always appear at the same time after the start of acquisition
for i in range(len(seq_list)):
seq_list[i][1]['delay_time'] = max(seq_list[i][0]['delay_time'] - 20.0, 0.1)
# Set some parameters independent of pulse sequence
globalparams = {
'acq_phase_cycle': '0 0 2 2 1 1 3 3 2 2 0 0 3 3 1 1',
'acquisition_time': 204.8, # us
'num_acqs': 4096, # "1D scans" in TNMR. Our 16-fold phase cycling means this should be a multiple of 16 for proper averaging
'ringdown_time': 5, # us
'post_acquisition_time': 100, # ms
'obs_freq': 42.09, # MHz
'nucleus': '139La', # For example, 139La. Will be used in a filename!
'comments': 'An example of a T2 scan',
'title': 'T2', # Goes at the beginning of a filename!
}
update_device_parameters(nmr_daq_scout, globalparams)
# Acquire data
scan_sequences(nmr_daq_scout, seq_list) # gather the data
+59
View File
@@ -0,0 +1,59 @@
# *****************************************************************************
#
# 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:
# Tina Arh <tina.arh@psi.ch>
#
# *****************************************************************************
# Select the parameters that are written under environment in HDF file at every write
SetEnvironment(tt, mf, nmr_TSSOP16, nmr_RP100Node_CH1, nmr_RP100Node_CH2, r1, tps)#, ZVLNode)
# PPMS: Set field & temperature
#maw(mf, 7.05) # set PPMS field (in Tesla)
#maw(tt, 60) # set PPMS temperature (in Kelvin)
#nicossleep(5*60) # 5 minutes
# Create the pulse sequence
pw90 = 1 # us
amp = 40 # percent
tau = 25 # us
# generate_pulse(pulse_width, amplitude, delay_time, pulse_cycle)
p90 = generate_pulse(pw90, amp, tau, '0 0 2 2 1 1 3 3 0 0 2 2 1 1 3 3')
p180_2 = generate_pulse(2*pw90, amp, 0, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3')
seq = [ p90, p180_2 ]
# Set some parameters independent of pulse sequence
globalparams = {
'acq_phase_cycle': '0 0 2 2 1 1 3 3 2 2 0 0 3 3 1 1',
'acquisition_time': 204.8, # us
'num_acqs': 128, # "1D scans" in TNMR. Our 16-fold phase cycling means this should be a multiple of 16 for proper averaging
'ringdown_time': 15, # us
'post_acquisition_time': 100, # ms
'obs_freq': 42.09, # MHz
'nucleus': '139La', # For example, 139La. Will be used in a filename!
'comments': 'Cooling from 50 K to 25 K',
'title': 'cooldown', # Goes at the beginning of a filename!
}
update_device_parameters(nmr_daq_scout, globalparams)
with tnmr_scan(): # Enters a mode of manual file control. Values will now be written into a single file until the context is lost
for i in range(10000):
scan_sequence(nmr_daq_scout, seq) # Acquire data
+66
View File
@@ -0,0 +1,66 @@
# *****************************************************************************
#
# 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 <davis.last@psi.ch>
# Tina Arh <tina.arh@psi.ch>
#
# *****************************************************************************
# Select the parameters that are written under environment in HDF file at every write
SetEnvironment(tt, mf, nmr_TSSOP16, nmr_RP100Node_CH1, nmr_RP100Node_CH2, r1, tps)#, ZVLNode)
# PPMS: Set field & temperature
#maw(tt, 3) # set PPMS temperature (in Kelvin)
#maw(mf, 3) # set the starting PPMS field (in Tesla)
#nicossleep(20*60) # 20 minutes
# ...
pw90 = 1 # us
amp = 40 # percent
tau = 20 # us
# generate_pulse(pulse_width, amplitude, delay_time, pulse_cycle)
p90 = generate_pulse(pw90, amp, tau, '0 0 2 2 1 1 3 3 0 0 2 2 1 1 3 3')
p180_2 = generate_pulse(2*pw90, amp, 0, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3')
seq = [ p90, p180_2 ]
# Set some parameters independent of pulse sequence
globalparams = {
'acq_phase_cycle': '0 0 2 2 1 1 3 3 2 2 0 0 3 3 1 1',
'acquisition_time': 204.8, # us
'num_acqs': 2048, # "1D scans" in TNMR. Our 16-fold phase cycling means this should be a multiple of 16 for proper averaging
'ringdown_time': 5, # us
'post_acquisition_time': 100, # ms
'obs_freq': 42.09, # MHz. Receiver frequency
'nucleus': 'NUCMgReS', # For example, 139La. Will be used in a filename!
'comments': 'An example of a field scan',
'title': 'Bscan', # Goes at the beginning of a filename!
}
update_device_parameters(nmr_daq_scout, globalparams)
fields = [ 6.95 + i*5e-2 for i in range(6) ]
# Estimate the length of scan and multiply by number of fields
print(f'Beginning scan. ETA: {timestring(estimate_sequence_length_from_device(nmr_daq_scout, seq)*len(fields))}')
with tnmr_scan(): # Enters a mode of manual file control. Values will now be written into a single file until the context is lost
for field in fields:
maw(mf, field) # assuming mf controls the reader's external field strength (PPMS, etc.)
scan_sequence(nmr_daq_scout, seq) # Acquire data
+76
View File
@@ -0,0 +1,76 @@
# *****************************************************************************
#
# 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:
# Tina Arh <tina.arh@psi.ch>
#
# *****************************************************************************
# Select the parameters that are written under environment in HDF file at every write
SetEnvironment(tt, mf, nmr_TSSOP16, nmr_RP100Node_CH1, nmr_RP100Node_CH2, r1, tps)#, ZVLNode)
# PPMS: Set field & temperature
#maw(tt, 3) # set PPMS temperature (in Kelvin)
#maw(mf, 3) # set the starting PPMS field (in Tesla)
#nicossleep(20*60) # 20 minutes
# ...
# Create the pulse sequence
pw90 = 2.5 # us
amp = 40 # percent
tau = 50 # us
# generate_pulse(pulse_width, amplitude, delay_time, pulse_cycle)
p90 = generate_pulse(pw90, amp, tau, '0 0 2 2 1 1 3 3 0 0 2 2 1 1 3 3')
p180_2 = generate_pulse(2*pw90, amp, 0, '1 3 3 1 2 0 0 2 0 2 2 0 3 1 1 3')
seq = [ p90, p180_2 ]
# Set some parameters independent of pulse sequence
globalparams = {
'acq_phase_cycle': '0 0 2 2 1 1 3 3 2 2 0 0 3 3 1 1',
'acquisition_time': 204.8, # us
'num_acqs': 16, # "1D scans" in TNMR. Our 16-fold phase cycling means this should be a multiple of 16 for proper averaging
'ringdown_time': 15, # us
'post_acquisition_time': 100, # ms
# 'obs_freq': 45.5, # MHz
'nucleus': 'NUCMgReS', # For example, 139La. Will be used in a filename!
'comments': 'An example of a frequency scan with manual tuning',
'title': 'FRscan', # Goes at the beginning of a filename!
}
update_device_parameters(nmr_daq_scout, globalparams)
# Choose the frequencies (in MHz)
central_frequency = 45.50 # MHz
frequency_range = 0.6 # MHz
half_N = 3
frequencies = [ central_frequency - frequency_range/2 + frequency_range * (i/(2*half_N)) for i in range(0, 2*half_N+1) ]
# Estimate the length of scan and multiply by number of frequencies
print(f'Beginning scan. ETA: {timestring(estimate_sequence_length_from_device(nmr_daq_scout, seq)*len(frequencies))}')
with tnmr_scan(): # Enters a mode of manual file control. Values will now be written into a single file until the context is lost
for fr in frequencies:
globalparams['obs_freq']=fr
update_device_parameters(nmr_daq_scout, globalparams)
# Pause to manually tune the frequency
print(f'New frequency: {fr:.2f} MHz')
pause(f'Tune the resonance circuit to {fr:.2f} MHz and click \'Continue script\' when you are done!')
scan_sequence(nmr_daq_scout, seq) # Acquire data
+26
View File
@@ -0,0 +1,26 @@
# *****************************************************************************
#
# 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 <davis.last@psi.ch>
#
# *****************************************************************************
[nicos]
logging_path = "/home/l_samenv/nicoslog/tnmr"
pid_path = "/home/l_samenv/nicoslog/tnmr"
setup_subdirs = [ "tnmr", "frappy_sinq"]
+26
View File
@@ -0,0 +1,26 @@
from os import environ
description = 'frappy main setup'
group = 'optional'
devices = {
'se_ppms':
device('nicos_sinq.frappy_sinq.devices.FrappyNode',
uri='pc12694:5000',
description='main SEC node', unit='',
prefix=environ.get('SE_PREFIX', ''), auto_create=True, service='main',
),
'timestamp': device('nicos_sinq.linse_nicos.timestamp.Timestamp', description='time, a dummy detector'),
}
startupcode = '''
printinfo("=======================================================================================")
printinfo("Welcome to the NICOS frappy secnode setup for PPMS!")
printinfo(" ")
printinfo("Usage:")
printinfo(" frappy(stick='<stick cfg>') # change sample-stick configuration")
printinfo(" frappy(addons='<addon1 cfg>,<addon2 cfg> ...') # change SE addons")
printinfo(" frappy(stick=None) # remove stick")
printinfo(" frappy(addons=None) # remove addons")
printinfo("=======================================================================================")
SetDetectors(timestamp)
'''
+18
View File
@@ -0,0 +1,18 @@
description = 'setup for the cache server'
group = 'special'
import os
insname = os.environ['INSTRUMENT'].rsplit('.')[-1]
devices = dict(
DB = device('nicos.services.cache.database.FlatfileCacheDatabase',
description = 'On disk storage for Cache Server',
storepath = os.path.join(os.environ.get('NICOS_CACHE', 'data'), insname, 'cache'),
loglevel = 'info',
makelinks = 'soft',
),
Server=device('nicos.services.cache.server.CacheServer',
db='DB',
server='localhost:%s' % os.environ['NICOS_CACHE_PORT'],
loglevel='info',
),
)
+3
View File
@@ -0,0 +1,3 @@
description = 'Generic configuration settings'
group = 'configdata'
+37
View File
@@ -0,0 +1,37 @@
description = 'setup for the execution daemon'
group = 'special'
import os
devices = dict(
# fixed list of users:
# first entry is the user name, second the hashed password, third the user
# level
# The user level are 'guest, 'user', and 'admin', ascending ordered in
# respect to the rights
# The entries for the password hashes are generated from randomized
# passwords and not reproduceable, please don't forget to create new ones:
# start python
# >>> import hashlib
# >>> hashlib.md5('password').hexdigest()
# or
# >>> hashlib.sha1('password').hexdigest()
Auth=device('nicos.services.daemon.auth.list.Authenticator',
# the hashing maybe 'md5' or 'sha1'
hashing='sha1',
passwd=[('guest', '',
'guest'),
('user', '21fb8406e5f81c24d4a5f5c7dd356e70a7288dc9',
'user'),
('admin', '76702e9ada292df094a875e5f72e9f778099d477',
'admin'),
],
),
Daemon=device('nicos.services.daemon.NicosDaemon',
server='0.0.0.0:%s' % os.environ['NICOS_DAEMON_PORT'],
authenticators=['Auth', ], # and/or 'UserDB'
loglevel='info',
),
)
startupcode = '''
'''
+24
View File
@@ -0,0 +1,24 @@
description = 'setup for the poller'
group = 'special'
import os
sysconfig = dict(
# use only 'localhost' if the cache is really running on the same machine,
# otherwise use the official computer name
cache='localhost:%s' % os.environ['NICOS_CACHE_PORT']
)
devices = dict(
Poller=device('nicos.services.poller.Poller',
autosetup=True,
poll=['system', 'setup'],
alwayspoll=[],
# setups that should be polled regardless if loaded
neverpoll=['frappy_main', 'frappy_stick', 'frappy_addons'],
# setups that should not be polled even if loaded
blacklist=[], # DEVICES that should never be polled
# (usually detectors or devices that have problems
# with concurrent access from processes)
loglevel='info'
),
)
+7
View File
@@ -0,0 +1,7 @@
description = 'NICOS LIN SE startup setup'
group = 'lowlevel'
startupcode = '''
print("============================================================")
print("Welcome to LIN SE tests and lab experiments")
'''
+68
View File
@@ -0,0 +1,68 @@
# basic frappy setup
description = 'system setup'
group = 'lowlevel'
import os
insname = os.environ['INSTRUMENT'].split('.')[-1]
sysconfig = dict(
cache = 'localhost:%s' % os.environ['NICOS_CACHE_PORT'],
instrument = 'instrument',
experiment = 'Exp',
datasinks = ['conssink', 'dmnsink' ],
notifiers = [],
)
modules = ['nicos.commands.standard', 'nicos_sinq.frappy_sinq.commands' ]
devices = dict(
instrument = device('nicos.devices.instrument.Instrument',
description = 'lab instrument %s' % insname,
instrument = insname,
responsible = 'whoever@wherever.ch',
website = '',
operators = ['whoever'],
facility = 'wherever',
),
Sample = device('nicos.devices.sample.Sample',
description = 'sample object',
),
Exp = device('nicos.devices.experiment.Experiment',
description = 'experiment object',
dataroot = os.path.join(os.environ['NICOS_DATA'], insname),
# proposalpath = 'data/%s' % insname,
sendmail = True,
serviceexp = 'service',
sample = 'Sample',
reporttemplate = '',
),
conssink = device('nicos.devices.datasinks.ConsoleScanSink'),
dmnsink = device('nicos.devices.datasinks.DaemonSink'),
Space = device('nicos.devices.generic.FreeSpace',
description = 'The amount of free space for storing data',
warnlimits = (5., None),
path = None,
minfree = 5,
),
LogSpace = device('nicos.devices.generic.FreeSpace',
description = 'Space on log drive',
path = os.path.join(os.environ['NICOS_LOG'], insname),
warnlimits = (.5, None),
minfree = 0.5,
lowlevel = True,
),
)
startupcode = '''
from nicos.core import SIMULATION
if not Exp.proposal and Exp._mode != SIMULATION:
try:
SetMode('master')
except Exception:
pass
else:
NewExperiment(0, 'demo experiment',
localcontact='Nico Suser <nico.suser@psi.ch>')
AddUser('Paul Scherrer <paul.scherrer@psi.ch')
NewSample('ExSample')
'''
+249
View File
@@ -0,0 +1,249 @@
from nicos.devices.datasinks import FileSink
from nicos.core.data.sink import DataSinkHandler
from nicos.core import Override, Param
from nicos.core.constants import POINT, SCAN, SUBSCAN
from nicos import session
import h5py as hdf
import numpy as np
import time
import datetime # For simple ISO8601 usage.
# A couple specific NeXus functionalities
def initialise_nexus_entry(file, index, timestamp):
'''file should be an HDF file object, which should be _open_. Timestamp should be an ISO8061 string, indicating the start of this entry, no longer than 48 characters.'''
entry_group = file.require_group(f'entry{index}')
entry_group.attrs['NX_class'] = 'NXentry'
entry_group.attrs['default'] = 'nmr_data'
d = entry_group.require_group(f'nmr_data')
d.attrs['NX_class'] = 'NXdata'
dets = entry_group.require_group(f'detectors')
dets.attrs['NX_class'] = 'NXdata'
env = entry_group.require_group(f'environment')
env.attrs['NX_class'] = 'NXdata'
startenv = entry_group.require_group(f'initial_environment')
startenv.attrs['NX_class'] = 'NXdata'
start_timestamp = entry_group.create_dataset('start_time', data=timestamp, dtype=hdf.string_dtype(length=48), shape=1)
end_timestamp = entry_group.create_dataset('end_time', data=timestamp, dtype=hdf.string_dtype(length=48), shape=1)
return entry_group
def choose_entry_from_datetime(file, datetime_iso):
'''file should be an HDF file object, which should be _open_. Returns the dataset, and a bool indicating if this is a newly created dataset.'''
entries = list(file.keys())
datetimes = []
tmp = []
# get groups which actually start with 'entry'
for i in entries:
if 'entry' in i:
tmp += [i]
starttime_str = np.array(file[i]['start_time'], 'S').tobytes().decode('utf-8')
if(datetime_iso == starttime_str[:len(datetime_iso)]):
return file[i], False # the appropriate entry was found
# did not find the appropriate entry, must create a new one.
entries = tmp
# Chop off the 'entry' part
entry_indices = [ int(i[5:]) for i in entries ]
# ... And get the last index.
if(len(entry_indices) == 0):
new_entry_index = 1
else:
new_entry_index = max(entry_indices) + 1
entry_group = initialise_nexus_entry(file, new_entry_index, datetime_iso)
return entry_group, True
# The actual workhorse
class HDF5ScanfileSinkHandler(DataSinkHandler):
def __init__(self, sink, dataset, detector):
super().__init__(sink, dataset, detector)
self._file = []
self._fname = None
self._template = sink.filenametemplate
hdf.get_config().track_order = True # keeps the order that objects are added in.
self.manager.assignCounter(self.dataset)
def prepare(self):
self._fname, self._filepaths = self.manager.getFilenames(self.dataset, self._template, self.sink.subdir)
for f in self._filepaths:
self._file += [ hdf.File(f, 'a', userblock_size=512) ] # make sure we can open it
self._file[-1].close()
def begin(self):
for f in self._filepaths:
with hdf.File(f, 'a') as file:
file.attrs['version'] = '100' # reserved for non-backwards-compatible changes!
# Side note on the file version: 100 is the first one. I am quite proud of my backwards compatibility so far! -DG
g = file.require_group('/metadata/')
try:
d = g.create_dataset('date', data=f'{time.time()} ({time.gmtime()})', dtype=hdf.string_dtype(length=128))
lc = g.create_dataset('local_contact', data=f'{session.experiment.localcontact}', dtype=hdf.string_dtype(length=128))
p = g.create_dataset('proposal', data=f'{session.experiment.proposal}', dtype=hdf.string_dtype(length=128))
u = g.create_dataset('users', data=f'{session.experiment.users}', dtype=hdf.string_dtype(length=128))
except:
pass
def putMetainfo(self, mi):
session.log.info('mi')
session.log.info(str(mi))
def __save_val(self, v, key, file, parent_group):
ret = None
if(isinstance(v, dict)):
pg = parent_group.require_group(str(key))
self.__save_dict(v, file, pg)
ret = pg
elif(isinstance(v, list) or isinstance(v, np.ndarray)):
varr = np.array(v)
ret = parent_group.require_dataset(str(key), data=varr, shape=varr.shape, dtype=varr.dtype, exact=False)
ret[:] = varr
elif(isinstance(v, str)):
ret = parent_group.require_dataset(str(key), data=v, shape=1, dtype=hdf.string_dtype(length=len(v) + 16), exact=False)
ret[0] = v
else:
varr = np.array([v])
ret = parent_group.require_dataset(str(key), data=varr, shape=varr.shape, dtype=varr.dtype, exact=False)
ret[:] = varr
return ret
def __save_dict(self, d, file, parent_group):
for key, val in d.items():
self.__save_val(val, key, file, parent_group)
def putValues(self, vals):
def validate_and_add(key_to_check, name, typ, group):
tags = key_to_check.split(':')
k = tags[-1]
if(name in tags[:-1] and ':' in key_to_check):
if(typ == 'list'):
if(name in group.attrs.keys()):
val_list = group.attrs[name]
if not(k in val_list):
max_len = len(val_list[0])
for i in val_list:
if(len(i) > max_len):
max_len = len(i)
print(max_len, val_list)
if(len(k) > max_len):
max_len = len(k)
new_arr = np.ndarray((len(group.attrs[name])+1,), dtype=f'<U{max_len}')
new_arr[:-1] = group.attrs[name]
new_arr[-1] = k
group.attrs[name] = new_arr
else:
group.attrs[name] = [ k ]
elif(typ == 'single'):
try:
group.attrs.modify(name, bytes(k, 'utf-8'))
except:
group.attrs[name] = bytes(k, 'utf-8')
return k
def write_val(key, val, d):
validate_and_add(key, 'axes', 'list', d)
validate_and_add(key, 'signal', 'single', d)
key = validate_and_add(key, 'auxiliary_signals', 'list', d)
newg = self.__save_val(val, key, file, d)
def write_time_val_pair(file, key, val_pair):
'''file should be an _open_ HDF file object. Writes a value to the appropriate dataset (based on key) and entry (based on timestamp in val_pair[0])'''
# get start datetime to find the correct entry:
timestamp = val_pair[0]
value = val_pair[1]
start_dt = datetime.datetime.fromtimestamp(timestamp)
start_dt_iso = str(start_dt.astimezone().isoformat())
# Get the correct entry (entryX, where X is an integer). Creates a new entry if necessary
g, new_dataset = choose_entry_from_datetime(file, start_dt_iso)
# update end time
et_dataset = g['end_time']
et_dataset[0] = str(datetime.datetime.now().astimezone().isoformat())
target_groups = [] # keys of groups to write to. Can be multiple for the existence of the initial_environment.
# choose the appropriate group within the entry
if(key in session.experiment.detlist):
target_groups += ['detectors']
if(key in session.experiment.envlist) or ('environment/' in key):
target_groups += ['environment']
if not(key in g['initial_environment'].keys()):
target_groups += ['initial_environment']
if('metadata/' in key):
target_groups += ['metadata']
if(len(target_groups) == 0):
target_groups += ['nmr_data']
for group_key in target_groups:
data_group = g.require_group(group_key)
# remove any special characters from the key
formatted_key = key
tagsplit = formatted_key.split(':')
tags = ':'.join(tagsplit[:-1]) # take everything before the last ':', these are used to signal special 'tags' on data, such as signal, axes, etc.
formatted_key = tagsplit[-1]
# remove the location specifier before the first '/' (multiple locations are not supported)
if('/' in formatted_key):
formatted_key = '/'.join(formatted_key.split('/')[1:])
formatted_key = tags + ':' + formatted_key # recombine...
#... for write_val to do its job
write_val(formatted_key, value, data_group)
for f in self._filepaths:
with hdf.File(f, 'a') as file:
dummytime = 0
for key, val in vals.items():
write_time_val_pair(file, key, val)
dummytime = val[0]
for key in session.experiment.detlist + session.experiment.envlist:
unread = True
v = 0
retries = 0
while(unread):
try:
v = session.getDevice(key).read()
unread = False
except:
retries += 1
if(retries > 10):
session.log.info(f'Failed to read {key}. Setting zero.')
v = 0
break
session.log.info(f'Trying to read {key} again')
pass
if(dummytime == 0):
dummytime = time.time()
val = (dummytime, v)
write_time_val_pair(file, key, val)
def putResults(self, quality, results):
pass
#session.log.info('HDF_NeXus putResults is not implemented! Use putValues.')
def addSubset(self, subset):
pass
#session.log.info('HDF_NeXus addSubset is not implemented! Use putValues.')
#def end(self):
# pass # nothing afaik needs to be done here.
class HDF5ScanfileSink(FileSink):
handlerclass = HDF5ScanfileSinkHandler
parameter_overrides = {
'settypes': Override(default=[SCAN, SUBSCAN]),
'filenametemplate': Override(default=['%(proposal)s_%(year)04d-%(month)02d-%(day)02d_%(hour)02d-%(minute)02d-%(second)02d.hdf'])
}