1 """! applying parameter values to detector compact.xml in hps-mc jobs"""
9 from ._parameter
import Parameter
10 from ._pattern
import Pattern
14 """! Abstract component to hold shared functionality for
15 compoents that edit the detectors in hps-java
17 **This component should never be used directly.**
22 java_dir = /full/path/to/hps-java
26 - **detector**: name of detector we are starting from
29 - **next_detector**: name of detector to write to
30 - **force**: ignore if the detector we are writing to already exists
53 return [
'force',
'next_detector']
56 return os.path.join(self.
java_dirjava_dir,
'detector-data',
'detectors', det_name)
59 """! deduce what the next detector should be given how the component has been configured
61 The component parameter **bump** is an argument here since it is only
62 a valid parameter for some components inheriting from this function.
65 self.
loggerlogger.info(
'Creating new detector directory.')
68 if not os.path.isdir(src_path):
69 raise ValueError(f
'Detector {self.detector} is not in hps-java ({src_path} not found)')
72 self.
loggerlogger.info(
'Deducing next detector name from current name')
74 matches = re.search(
'.*iter([0-9]*)', self.
detectordetector)
76 raise ValueError(
'No "iterN" suffix on detector name.')
78 i = int(matches.group(1))
81 self.
loggerlogger.info(f
'Creating new detector named "{self.next_detector}"')
83 self.
loggerlogger.info(f
'Operating on assumed-existing detector "{self.detector}"')
86 def _to_compact(self, parameter_set, detname, save_prev=True, prev_ext='prev'):
87 """! write the input parameter set into the input compact.xml file
89 Update the millepede parameters in the destination compact.xml with the
90 parameters stored in the parameter_set map.
95 dict mapping parameter ID number to Parameter instance
97 name of detector whose compact.xml we should edit
98 save_prev : bool, optional
99 whether to save a copy of the original compact.xml before we edited it
100 prev_ext : str, optional
101 extension to add to the original compact.xml if it is being saved
104 def _change_xml_value(line, key, new_val, append=True):
105 """!change an XML line to have a new value
107 Assuming that the key and value are on the same line,
108 we can do some simple string arithmetic to find which
109 part of the string needs to be replaced.
113 xml-stuff key="value" other-xml-stuff
115 We make the replacement by finding the location of 'key'
116 in the line, then finding the next two quote characters.
117 The stuff in between those two quote characters is replaced
118 or appended with new_val and everything else in the line
121 The updated line is returned as a new string.
124 i_key = line.find(key)
125 pre_value = line[:i_key]
126 post_value = line[i_key:]
128 quote_open = post_value.find(
'"')+1
129 pre_value += post_value[:quote_open]
130 post_value = post_value[quote_open:]
132 quote_close = post_value.find(
'"')
133 og_value = post_value[:quote_close]
134 post_value = post_value[quote_close:]
136 new_value = f
'{new_val}'
138 new_value = f
'{og_value} {new_val}'
140 return f
'{pre_value}{new_value}{post_value}'
143 dest = os.path.join(self.
_detector_dir_detector_dir(detname),
'compact.xml')
144 if not os.path.isfile(dest):
145 raise ValueError(f
'{detname} does not have a compact.xml to modify.')
146 self.
loggerlogger.info(f
'Writing compact.xml at {dest}')
147 original_cp = dest +
'.' + prev_ext
148 shutil.copy2(dest, original_cp)
150 with open(dest,
'w')
as f:
151 with open(original_cp)
as og:
153 if 'info name' in line:
155 self.
loggerlogger.debug(f
'Changing detector name to {detname}')
156 f.write(_change_xml_value(line,
'name', detname, append=
False))
160 if 'millepede_constant' not in line:
165 for i
in parameter_set:
168 self.
loggerlogger.debug(f
'Changing parameter {i}')
169 f.write(_change_xml_value(
170 line,
'value', parameter_set[i].compact_value(), append=
True
180 os.remove(original_cp)
183 """! Update the readme for the passed detector name
185 Includes a timestamp at the end of the passed message.
189 log_path = os.path.join(self.
_detector_dir_detector_dir(detname),
'README.md')
190 self.
loggerlogger.info(f
'Updating README.md at {log_path}')
191 with open(log_path,
'a')
as log:
192 from datetime
import datetime
193 log.write(f
'\n# {detname}\n')
195 log.write(f
'_auto-generated note on {str(datetime.now())}_\n')
201 """! Apply a millepede.res file to a detector description
203 This job component loads a result file into memory and the
204 goes line-by-line through a detector description, updating
205 the lines with any parameters that have updated values in the
211 java_dir = /full/path/to/hps-java
215 - **detector**: name of detector to apply parameters to
218 - **res\\_file**: path to millepede results file (default: 'millepede.res')
219 - **bump**: generate the next detector name by incrementing the iter number of the input detector (default: True)
220 - **force**: override the next detector path (default: False)
221 - **next\\_detector**: provide name of next detector, preferred over **bump** if provided (default: None)
238 return 'custom python execute'
245 if os.path.isdir(dest_path)
and not self.
forceforce:
246 raise ValueError(f
'Detector {self.next_detector} already exists and so it cannot be created. Use "force" to overwrite an existing detector.')
254 if os.path.isdir(dest_path):
255 shutil.rmtree(dest_path)
261 if os.path.isfile(lcdd_file):
266 if os.path.isfile(properties_file):
267 os.remove(properties_file)
270 parameters = Parameter.parse_pede_res(self.
res_fileres_file, skip_nonfloat=
True)
272 self.
loggerlogger.debug(f
'Applying pede results: {parameters}')
276 Compact updated by applying results from a run of pede
278 ### Parameters Floated
280 {json.dumps(self.to_float, indent = 2)}
288 """! write a detector intentionally misaligned relative to another one
293 java_dir = /full/path/to/hps-java
294 param_map = /full/path/to/parameter/map.txt
298 - **detector** : name of detector to base our misalignment on
299 (and write to if no **next\\_detector** is given)
300 - **parameters** : dictionary of parameters to the change that should be applied
301 - each key in this dictionary is a hpsmc.alignment._pattern.Pattern so it can specify a single parameter or a group of parameters
311 super().
__init__(
'WriteMisalignedDet')
320 return 'custom python execute'
325 (
Pattern(parameter_str), val_change)
326 for parameter_str, val_change
in self.
parametersparameters.items()
329 full_parameters = Parameter.parse_map_file(self.
param_mapparam_map)
331 parameters_to_apply = {}
332 for idn, param
in full_parameters.items():
333 for pattern, val_change
in patterns:
334 if pattern.match(param):
335 param._val = val_change
336 parameters_to_apply[idn] = param
342 if not os.path.isdir(src_det):
343 raise ValueError(f
'{src_det} detector does not exist.')
346 if dest_same_as_src
and not self.
forceforce:
347 raise ValueError(f
'Need to explicitly use the "force" parameter if you want to write to an existing detector.')
349 if not dest_same_as_src:
351 if not os.path.isdir(dest_det):
352 shutil.copytree(src_det, dest_det)
353 elif not self.
forceforce:
354 raise ValueError(
'{dest_det} detector already exists. Use "force" if you want to write to an existing detector.')
359 Detector written by applying an intentional misalignment to {self.detector}.
361 ### Misalignment Applied
363 {json.dumps(self.parameters, indent=2)}
370 """! construct an LCDD from a compact.xml and recompile necessary parts of hps-java
372 This is a Component interface to the hps-mc-construct-detector script.
377 java_dir = /full/path/to/hps-java
378 hps_java_bin_jar = /full/path/to/hps-java/bin.jar
382 - **detector**: name of detector to construct (unless next\\_detector is provided)
385 - **bump**: generate the next detector name by incrementing the iter number of the input detector (default: True)
386 - **force**: override the next detector path (default: False)
387 - **next\\_detector**: provide name of next detector, preferred over **bump** if provided (default: None)
401 super().
__init__(
'ConstructDetector',
402 command=
'hps-mc-construct-detector')
411 """Called after configured but before running
413 We deduce which detector we will be running with,
414 attempting to mimic the logic in ApplyPedeRes.execute
415 so that we compile the same detector that pede results
Apply a millepede.res file to a detector description.
def execute(self, log_out, log_err)
Generic component execution method.
def optional_parameters(self)
Return a list of optional parameters.
construct an LCDD from a compact.xml and recompile necessary parts of hps-java
def required_config(self)
Return a list of required configuration settings.
def optional_parameters(self)
Return a list of optional parameters.
def cmd_args(self)
Return the command arguments of this component.
write a detector intentionally misaligned relative to another one
def required_config(self)
Return a list of required configuration settings.
def required_parameters(self)
Return a list of required parameters.
def execute(self, out, err)
Generic component execution method.
Abstract component to hold shared functionality for compoents that edit the detectors in hps-java.
def required_config(self)
Return a list of required configuration settings.
def __init__(self, name, **kwargs)
def required_parameters(self)
Return a list of required parameters.
def _update_readme(self, detname, msg)
Update the readme for the passed detector name.
def optional_parameters(self)
Return a list of optional parameters.
def _detector_dir(self, det_name)
def _deduce_next_detector(self, bump=False)
deduce what the next detector should be given how the component has been configured
def _to_compact(self, parameter_set, detname, save_prev=True, prev_ext='prev')
write the input parameter set into the input compact.xml file
Pattern that can match one or more paramters.
Base class for components in a job.