1"""! applying parameter values to detector compact.xml in hps-mc jobs""" 
    9from ._parameter 
import Parameter
 
   10from ._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_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.
logger.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.
logger.info(
'Deducing next detector name from current name')
 
   74                matches = re.search(
'.*iter([0-9]*)', self.
detector)
 
   76                    raise ValueError(
'No "iterN" suffix on detector name.')
 
   78                    i = int(matches.group(1))
 
   81            self.
logger.info(f
'Creating new detector named "{self.next_detector}"')
 
   83            self.
logger.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(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.
logger.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.
logger.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.
logger.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(detname), 
'README.md')
 
  190        self.
logger.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.
force:
 
  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_file, skip_nonfloat=
True)
 
  272        self.
logger.debug(f
'Applying pede results: {parameters}')
 
  276Compact 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.
parameters.items()
 
  329        full_parameters = Parameter.parse_map_file(self.
param_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.
force:
 
  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)
 
  354                raise ValueError(
'{dest_det} detector already exists. Use "force" if you want to write to an existing detector.')
 
  359Detector 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.
execute(self, log_out, log_err)
Generic component execution method.
optional_parameters(self)
Return a list of optional parameters.
construct an LCDD from a compact.xml and recompile necessary parts of hps-java
optional_parameters(self)
Return a list of optional parameters.
required_config(self)
Return a list of required configuration settings.
cmd_args(self)
Return the command arguments of this component.
write a detector intentionally misaligned relative to another one
required_parameters(self)
Return a list of required parameters.
execute(self, out, err)
Generic component execution method.
required_config(self)
Return a list of required configuration settings.
Abstract component to hold shared functionality for compoents that edit the detectors in hps-java.
_to_compact(self, parameter_set, detname, save_prev=True, prev_ext='prev')
write the input parameter set into the input compact.xml file
_deduce_next_detector(self, bump=False)
deduce what the next detector should be given how the component has been configured
optional_parameters(self)
Return a list of optional parameters.
required_parameters(self)
Return a list of required parameters.
_detector_dir(self, det_name)
required_config(self)
Return a list of required configuration settings.
__init__(self, name, **kwargs)
_update_readme(self, detname, msg)
Update the readme for the passed detector name.
Pattern that can match one or more paramters.
Base class for components in a job.