1
- """
2
- Rules for LOQ
3
- """
4
-
5
1
from __future__ import annotations
6
2
7
- import logging
8
3
import re
9
- import typing
10
4
from dataclasses import dataclass
11
- from pathlib import Path
5
+ from typing import TYPE_CHECKING
12
6
13
7
import requests
14
8
import xmltodict
15
9
10
+ from rundetection .rules .common_rules import logger
16
11
from rundetection .rules .rule import Rule
17
12
18
- if typing . TYPE_CHECKING :
13
+ if TYPE_CHECKING :
19
14
from rundetection .job_requests import JobRequest
20
15
21
- logger = logging .getLogger (__name__ )
22
-
23
16
24
17
@dataclass
25
18
class SansFileData :
@@ -28,22 +21,23 @@ class SansFileData:
28
21
run_number : str
29
22
30
23
31
- def _extract_run_number_from_filename (filename : str ) -> str :
32
- # Assume filename looks like so: LOQ00100002.nxs, then strip.
33
- return filename .split ("." )[0 ].lstrip ("LOQ" ).lstrip ("0" )
34
-
35
-
36
24
def _is_sample_transmission_file (sans_file : SansFileData , sample_title : str ) -> bool :
37
25
return sample_title in sans_file .title and sans_file .type == "TRANS"
38
26
39
27
40
28
def _is_sample_direct_file (sans_file : SansFileData ) -> bool :
41
- return ("direct" in sans_file .title .lower () or "empty" in sans_file .title .lower ()) and sans_file .type == "TRANS"
29
+ return (
30
+ "direct" in sans_file .title .lower ()
31
+ or "empty" in sans_file .title .lower ()
32
+ or "mt " in sans_file .title .lower ()
33
+ or " mt" in sans_file .title .lower ()
34
+ or sans_file .title .lower () == "{mt}"
35
+ ) and sans_file .type == "TRANS"
42
36
43
37
44
38
def _is_can_scatter_file (sans_file : SansFileData , can_title : str ) -> bool :
45
39
title_contents = re .findall (r"{.*?}" , sans_file .title )
46
- return len (title_contents ) == 1 and can_title == title_contents [0 ] and sans_file .type == "SANS/TRANS"
40
+ return len (title_contents ) == 1 and can_title == title_contents [0 ] and sans_file .type in { "SANS/TRANS" , "SANS" }
47
41
48
42
49
43
def _is_can_transmission_file (sans_file : SansFileData , can_title : str ) -> bool :
@@ -79,24 +73,15 @@ def _find_can_trans_file(sans_files: list[SansFileData], can_title: str) -> Sans
79
73
return None
80
74
81
75
82
- def find_path_for_run_number (cycle_path : str , run_number : int ) -> Path | None :
83
- # 10 is just a magic number, but we needed an unrealistic value for the maximum
84
- for padding in range (11 ):
85
- potential_path = Path (f"{ cycle_path } /LOQ{ str (run_number ).zfill (padding )} .nxs" )
86
- if potential_path .exists ():
87
- return potential_path
88
- return None
89
-
90
-
91
- def grab_cycle_instrument_index (cycle : str ) -> str :
76
+ def grab_cycle_instrument_index (cycle : str , instrument : str ) -> str :
92
77
_ , cycle_year , cycle_num = cycle .split ("_" )
93
- url = f"http://data.isis.rl.ac.uk/journals/ndxloq /journal_{ cycle_year } _{ cycle_num } .xml"
78
+ url = f"http://data.isis.rl.ac.uk/journals/ndx { instrument . lower () } /journal_{ cycle_year } _{ cycle_num } .xml"
94
79
return requests .get (url , timeout = 5 ).text
95
80
96
81
97
82
def create_list_of_files (job_request : JobRequest ) -> list [SansFileData ]:
98
83
cycle = job_request .additional_values ["cycle_string" ]
99
- xml = grab_cycle_instrument_index (cycle = cycle )
84
+ xml = grab_cycle_instrument_index (cycle = cycle , instrument = job_request . instrument )
100
85
cycle_run_info = xmltodict .parse (xml )
101
86
list_of_files = []
102
87
for run_info in cycle_run_info ["NXroot" ]["NXentry" ]:
@@ -121,57 +106,103 @@ def _set_transmission_file(job_request: JobRequest, sample_title: str, sans_file
121
106
if not job_request .additional_values ["included_trans_as_scatter" ]:
122
107
trans_file = _find_trans_file (sans_files = sans_files , sample_title = sample_title )
123
108
trans_run_number = trans_file .run_number if trans_file is not None else None
124
- logger .info ("LOQ trans found %s" , trans_run_number )
109
+ logger .info ("%s trans found %s" , job_request . instrument , trans_run_number )
125
110
else :
126
111
trans_run_number = str (job_request .run_number )
127
- logger .info ("LOQ trans set as scatter %s" , trans_run_number )
112
+ logger .info ("%s trans set as scatter %s" , job_request . instrument , trans_run_number )
128
113
if trans_run_number is not None :
129
114
job_request .additional_values ["scatter_transmission" ] = trans_run_number
130
115
131
116
132
117
def _set_can_files (can_title : str | None , job_request : JobRequest , sans_files : list [SansFileData ]) -> None :
133
118
if can_title is not None :
134
119
can_scatter = _find_can_scatter_file (sans_files = sans_files , can_title = can_title )
135
- logger .info ("LOQ can scatter found %s" , can_scatter )
120
+ logger .info ("%s can scatter found %s" , job_request . instrument , can_scatter )
136
121
if can_scatter is not None :
137
122
job_request .additional_values ["can_scatter" ] = can_scatter .run_number
138
123
139
124
# If using M4 monitor then can scatter is the transmission
140
125
if not job_request .additional_values ["included_trans_as_scatter" ]:
141
126
can_trans = _find_can_trans_file (sans_files = sans_files , can_title = can_title )
142
- logger .info ("LOQ can trans found %s" , can_trans )
127
+ logger .info ("%s can trans found %s" , job_request . instrument , can_trans )
143
128
else :
144
129
can_trans = can_scatter
145
- logger .info ("LOQ can trans set as scatter %s" , can_scatter )
130
+ logger .info ("%s can trans set as scatter %s" , job_request . instrument , can_scatter )
146
131
if can_trans is not None and can_scatter is not None :
147
132
job_request .additional_values ["can_transmission" ] = can_trans .run_number
148
133
149
134
150
135
def _set_direct_files (job_request : JobRequest , sans_files : list [SansFileData ]) -> None :
151
136
direct_file = _find_direct_file (sans_files = sans_files )
152
- logger .info ("LOQ direct files found %s" , direct_file )
137
+ logger .info ("%s direct files found %s" , job_request . instrument , direct_file )
153
138
if direct_file is not None :
154
139
if "scatter_transmission" in job_request .additional_values :
155
140
job_request .additional_values ["scatter_direct" ] = direct_file .run_number
156
141
if "can_scatter" in job_request .additional_values and "can_transmission" in job_request .additional_values :
157
142
job_request .additional_values ["can_direct" ] = direct_file .run_number
158
143
159
144
160
- class LoqFindFiles (Rule [bool ]):
145
+ class CheckIfScatterSANS (Rule [bool ]):
146
+ def __init__ (self , value : bool ):
147
+ super ().__init__ (value )
148
+ self .should_be_first = True
149
+
150
+ def verify (self , job_request : JobRequest ) -> None :
151
+ if not job_request .experiment_title .endswith ("_SANS/TRANS" ) and not job_request .experiment_title .endswith (
152
+ "_SANS"
153
+ ):
154
+ job_request .will_reduce = False
155
+ logger .info ("Not a scatter run. Does not have _SANS or _SANS/TRANS at the end of the experiment title." )
156
+ return
157
+ # If it is a direct fix, sans or trans, it should fail, which is why hard coded TRANS as we want to check
158
+ # part of the logic not all.
159
+ if _is_sample_direct_file (
160
+ SansFileData (title = job_request .experiment_title , type = "TRANS" , run_number = str (job_request .run_number ))
161
+ ):
162
+ job_request .will_reduce = False
163
+ logger .info ("File is an empty cell or direct beam scatter run, and should not be processed" )
164
+ return
165
+ if "{" not in job_request .experiment_title and "}" not in job_request .experiment_title :
166
+ job_request .will_reduce = False
167
+ logger .info (
168
+ "Not a parsable scatter title, a scatter contains {} in format {x}_{y}_SANS/TRANS. or {x}_SANS/TRANS."
169
+ )
170
+ return
171
+
172
+
173
+ class SansSliceWavs (Rule [str ]):
174
+ """
175
+ This rule enables users to set the SliceWavs for each script
176
+ """
177
+
178
+ def verify (self , job_request : JobRequest ) -> None :
179
+ job_request .additional_values ["slice_wavs" ] = self ._value
180
+
181
+
182
+ class SansPhiLimits (Rule [str ]):
183
+ """
184
+ This rule enables users to set the PhiLimits for each script
185
+ """
186
+
187
+ def verify (self , job_request : JobRequest ) -> None :
188
+ job_request .additional_values ["phi_limits" ] = self ._value
189
+
190
+
191
+ class SansFindFiles (Rule [bool ]):
161
192
def __init__ (self , value : bool ):
162
193
super ().__init__ (value )
163
194
self ._should_be_last = True
164
195
165
196
def verify (self , job_request : JobRequest ) -> None :
166
197
title = job_request .experiment_title
167
- logger .info ("LOQ title is %s" , title )
198
+ logger .info ("%s title is %s" , job_request . instrument , title )
168
199
# Find all of the "titles" [0] is the scatter, [1] is the background
169
200
title_parts = re .findall (r"{.*?}" , title )
170
201
sample_title = title_parts [0 ]
171
- logger .info ("LOQ sample title is %s" , sample_title )
202
+ logger .info ("%s sample title is %s" , job_request . instrument , sample_title )
172
203
# If background was defined in the title set can title
173
204
can_title = title_parts [1 ] if len (title_parts ) > 1 else None
174
- logger .info ("LOQ can title is %s from list %s" , can_title , title_parts )
205
+ logger .info ("%s can title is %s from list %s" , job_request . instrument , can_title , title_parts )
175
206
176
207
# Get the file lists
177
208
sans_files = create_list_of_files (job_request )
@@ -188,8 +219,8 @@ def verify(self, job_request: JobRequest) -> None:
188
219
_set_direct_files (job_request , sans_files )
189
220
190
221
191
- class LoqUserFile (Rule [str ]):
222
+ class SansUserFile (Rule [str ]):
192
223
def verify (self , job_request : JobRequest ) -> None :
193
224
# If M4 in user file then the transmission and scatter files are the same.
194
225
job_request .additional_values ["included_trans_as_scatter" ] = "_M4" in self ._value
195
- job_request .additional_values ["user_file" ] = f"/extras/loq /{ self ._value } "
226
+ job_request .additional_values ["user_file" ] = f"/extras/{ job_request . instrument . lower () } /{ self ._value } "
0 commit comments