-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSConfiguration.py
More file actions
184 lines (159 loc) · 5.42 KB
/
Copy pathSConfiguration.py
File metadata and controls
184 lines (159 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# SConfiguration class definition
import os.path
from statuses import NOTSUBMITTED, PROCESSING, SUBMITTED, SCRIPTS_GENERATED
class SConfiguration():
"""Steering card configuration for a simulation submission."""
def __init__(self, scardFile):
"""Initialize from a scard text file."""
self._init_fields()
self.file = scardFile
if not os.path.isfile(scardFile):
raise FileNotFoundError('Scard file not found: {}'.format(scardFile))
with open(scardFile) as f:
self.parseSCard(f.read())
@classmethod
def from_string(cls, scard_text):
"""Initialize from a scard text string (e.g. fetched from the database)."""
obj = cls.__new__(cls)
obj._init_fields()
obj.parseSCard(scard_text)
return obj
def _init_fields(self):
"""Set all scard attributes to None."""
self.file = None
self.project = None
self.type = None
self.connection = None
self.version = None
self.username = None
self.configuration = None
self.generator = None
self.genOptions = None
self.nevents = None
self.njobs = None
self.jobs = None
self.client_ip = None
self.fields = None
self.torus = None
self.solenoid = None
self.bkmerging = None
self.softwarev = None
self.dstOUT = None
self.zposition = None
self.raster = None
self.beam = None
self.vertex_choice = None
self.string_id = None
self.output_type = None
self.submission = None
self.runs = None
self.run_list = None
self.gemcv = None
self.coatjavav = None
self.mcgenv = None
self.genExecutable = None
self.user_string = None
self._extra = {}
def parseSCard(self, scardContent):
"""Parse scard key: value lines into attributes.
Unknown keys are stored in _extra rather than raising an error,
so the class stays forward-compatible with new scard fields.
After parsing, _resolve_type() is called to ensure self.type is
always set even when the scard omits the type field.
"""
label = self.file if self.file else 'string'
for raw_line in scardContent.splitlines():
line = raw_line.strip()
if not line:
continue
pos = line.find(':')
if pos < 0:
continue
key = line[:pos].strip()
value = line[pos + 1:].strip()
if not key or key == 'file':
continue
if hasattr(self, key) and not key.startswith('_'):
setattr(self, key, value)
else:
self._extra[key] = value
self._resolve_type()
self._resolve_software_versions()
print('SConfiguration: parsed {} successfully (type {})'.format(label, self.type))
def _resolve_software_versions(self):
"""Populate gemcv, coatjavav, mcgenv from softwarev; torus/solenoid from fields."""
if self.softwarev:
for token in self.softwarev.split():
if '/' not in token:
continue
name, _, version = token.partition('/')
if name == 'gemc':
self.gemcv = version
elif name == 'coatjava':
self.coatjavav = version
elif name == 'mcgen':
self.mcgenv = version
if self.fields and (self.torus is None or self.solenoid is None):
parts = self.fields.split('_')
if len(parts) == 2:
tor_part, sol_part = parts
if tor_part.startswith('tor'):
self.torus = tor_part[3:]
if sol_part.startswith('sol'):
self.solenoid = sol_part[3:]
def _resolve_type(self):
"""Set self.type to '1' or '2' when the scard does not include it.
Type 1 — generator-based: the generator field holds a known executable
name (e.g. 'clasdis', 'dvcs', 'pythia'). The node runs the
generator to produce events, then feeds them to gemc.
Type 2 — lund-file-based: the generator field holds a filesystem path
or URL pointing to a directory of pre-generated lund files
(starts with '/' or contains '://'). Each subjob reads one
lund file; the number of jobs equals the number of files.
If type is already set explicitly in the scard it is left unchanged.
"""
if self.type is not None:
return
if self.generator and (
self.generator.startswith('/') or '://' in self.generator
):
self.type = '2'
else:
self.type = '1'
def show(self):
"""Print all fields in aligned format."""
known = [
('file', self.file),
('project', self.project),
('type', self.type),
('connection', self.connection),
('version', self.version),
('username', self.username),
('configuration', self.configuration),
('generator', self.generator),
('genOptions', self.genOptions),
('nevents', self.nevents),
('njobs', self.njobs),
('client_ip', self.client_ip),
('fields', self.fields),
('torus', self.torus),
('solenoid', self.solenoid),
('bkmerging', self.bkmerging),
('softwarev', self.softwarev),
('dstOUT', self.dstOUT),
('zposition', self.zposition),
('raster', self.raster),
('beam', self.beam),
('vertex_choice', self.vertex_choice),
('string_id', self.string_id),
('output_type', self.output_type),
('submission', self.submission),
('runs', self.runs),
('run_list', self.run_list),
]
extra = [(k, v) for k, v in self._extra.items()]
all_fields = known + extra
width = max(len(k) for k, _ in all_fields)
print('SConfiguration:')
for key, value in all_fields:
print(' {} : {}'.format(key.ljust(width), value))