3 from DQUtils 
import process_iovs
 
    4 from DQUtils.sugar 
import IOVSet, define_iov_type
 
    5 from DQUtils.ext 
import tally
 
    7 import DCSCalculator2.config 
as config
 
    8 from .variable 
import GoodIOV, DCSC_Variable_With_Mapping, DefectIOV
 
    9 from .consts 
import (WHITE, GREY, RED, YELLOW, GREEN, 
 
   10                      EMPTY, OUT_OF_CONFIG, BAD, GOOD)
 
   13 log = logging.getLogger(
"DCSCalculator2.subdetector")
 
   14 logEnabledFor = log.isEnabledFor
 
   18 def DCSOFL_IOV(channel, Code, deadFrac, Thrust, NConfig, NWorking):
 
   19     "DCS calculator result iov type" 
   23     A defect calculator for one subdetector. 
   29         if not hasattr(self, 
"input_to_output_map") 
and hasattr(self, 
"mapping"):
 
   33                            for key, values 
in self.mapping.
items() 
 
   39         return self.__class__.__name__
 
   43         Get a DCS_Variable by name. 
   45         for variable 
in self.variables:
 
   46             if variable.folder_name == name:
 
   49         raise RuntimeError(
"Folder '%s' not found" % name)
 
   53         Set mapping of input channels for a DCSC_Variable_With_Mapping. 
   54         Some input folders have different channel numbering conventions. 
   58         if not isinstance(variable, DCSC_Variable_With_Mapping):
 
   59             raise RuntimeError(
"'%s' is not a DCSC_Variable_With_Mapping!")
 
   61         variable.input_channel_map = mapping
 
   65         Read the cool database and determine the state of the input channels 
   74         inputs = [v.calculate_good_iovs(lbtime, self) 
for v 
in self.variables]
 
   77         if log.isEnabledFor(logging.INFO):
 
   78             log.info(
"lbtime hash = % 09x, inputs hash = %09x",
 
   79                      hash(lbtime), 
hash(tuple(self.variables)))
 
   84         Merge input channel states across variables, taking the worst. 
   86         For detector configuration variables, it is assumed that if no IoV  
   87         exists for that channel in this variable, then it is in a good state. 
  100         for state, variable 
in zip(states, self.variables):
 
  101             state = state.good 
if state 
else None 
  102             if state == OUT_OF_CONFIG:
 
  103                 assert variable.is_config_variable, 
"OOC without is_config_variable!" 
  107             elif state 
is None or state < result:
 
  108                 if state 
is WHITE 
and variable.is_config_variable:
 
  122         Merge multiple variables together for one input channel. 
  123         Each 'inputs' arg is an IOVSet that corresponds to this input channel. 
  132             result.add(since, until, channel, state)
 
  134         return result.solidify(GoodIOV)
 
  138         Join up the information which was used to make a decision across  
  143             info = tuple(state._orig_iov[3:] 
for state 
in states)
 
  144             result.add(since, until, channel, info)
 
  146         return result.solidify(GoodIOV) 
 
  151         Merge multiple variables together for many channels. 
  152         Takes a list of IOVSets, one for each DCSC_Variable. 
  156         info_states = IOVSet(iov_type=GoodIOV)
 
  160         inputs = [iovs.by_channel 
for iovs 
in inputs]
 
  163         all_channels = 
sorted(
set(y 
for x 
in inputs 
for y 
in x.keys()))
 
  165         tally_system_states = config.opts.tally_system_states
 
  167         for channel 
in all_channels:
 
  170             c_inputs = [x[channel] 
for x 
in inputs]
 
  175             if tally_system_states:
 
  178                 info_states.extend(info_state)
 
  181         if tally_system_states:
 
  183             from DQUtils.ext 
import tally
 
  185                 return "/".
join(x[0] 
for x 
in state)
 
  187             chans, iovs = zip(*
sorted(info_states.by_channel.items()))
 
  189                 if states[0]._is_empty:
 
  193                 statetally = 
tally(
pretty(x.good) 
for x 
in states[1:])
 
  195                 print(since, until, statetally)
 
  201         Determine which input channels belong to which output channels. 
  202         inputs is a list of IOVSets, exactly one per channel. 
  211         seen_channels = 
set()
 
  213         empty_iovset_types = [iovs.empty_maker() 
for iovs 
in inputs]
 
  221             if not iovs: 
continue  
  222             input_channel = iovs[0].channel
 
  223             if input_channel 
not in mapping: 
 
  224                 raise RuntimeError(
"channel not found in mapping: " + 
str(input_channel))
 
  225             seen_channels.add(input_channel)
 
  226             output_channel = mapping[input_channel]
 
  227             result.setdefault(output_channel, []).
append(iovs)
 
  232         for channel, make_iovset 
in zip(missing_channels, empty_iovset_types):
 
  234             output_channel = mapping[channel]
 
  235             result.setdefault(output_channel, []).
append(make_iovset())
 
  244         Return a set containing the all input channel IDs for this subdetector 
  246         return set(v 
for vals 
in self.mapping.
values() 
for v 
in vals)
 
  250         If it is possible to give a logical name for an input channel, return 
  251         it here. These numbers are used for debugging purposes. 
  253         By default, do nothing. Over-ridden by subdetectors 
  258         indices = [i 
for i, x 
in enumerate(states) 
if x 
is what]
 
  261         return [input_chan_name(chan_indices[i]) 
for i 
in indices]
 
  266         Calculate the dead fraction and the resulting traffic light code. 
  271         n_total    = len(states)
 
  272         n_working  = states.count(GOOD)
 
  273         n_bad      = states.count(BAD)
 
  274         n_ooc      = states.count(OUT_OF_CONFIG)
 
  275         n_unfilled = states.count(EMPTY)
 
  277         log.debug(
"%s -> %s tot:%4s working:%4s bad:%4s ooc:%4s empty:%4s",
 
  278                      since, until, n_total, n_working, n_bad, n_ooc, n_unfilled)
 
  284         assert n_total == len(self.mapping[output_channel])
 
  288                 args = output_channel, states, EMPTY
 
  290                 log.debug(
"WARNING: the following channelids are unfilled: " 
  291                           "%r", unfilled_chans)
 
  295                 log.debug(
"OOC ids: %s", 
sorted(ooc_ids))
 
  298                 log.debug(
"BAD ids: %s", 
sorted(bad_ids))
 
  300         assert not n_total - n_working - n_bad - n_ooc - n_unfilled, (
 
  301             "Some states unaccounted for? This is a bug.")
 
  303         n_config = n_total - n_ooc
 
  304         dead_fraction = 1. - n_working / n_total
 
  307         if self.dead_fraction_caution 
is not None and (
 
  308            self.dead_fraction_caution < dead_fraction <= self.dead_fraction_bad):
 
  311         elif dead_fraction > self.dead_fraction_bad:
 
  316         unfilled_fraction = n_unfilled / n_total
 
  317         threshold = (self.dead_fraction_caution 
 
  318                      if self.dead_fraction_caution 
is not None else  
  319                      self.dead_fraction_bad)
 
  320         if unfilled_fraction > threshold:
 
  323         if n_unfilled 
and config.opts.mark_unfilled_grey:
 
  328         return code, dead_fraction, thrust, n_config, n_working
 
  331         """Apparently not used""" 
  332         changes = [(a, b) 
for a, b 
in zip(prev_states, states) 
if a != b]
 
  333         log.debug(
"Changes at %s: %i: %s", runlb, len(changes), 
tally(changes))
 
  337         log.debug(
"Calculating dead fractions for output: %i", output_channel)
 
  345         dead_frac_iovs = IOVSet()
 
  352             state_iovs = states[1:]
 
  354             states = [s.good 
for s 
in state_iovs]
 
  356             if run_iov._is_empty:
 
  360             iov_state = calc_dead_frac(since, until, output_channel, 
 
  364             result_iov = 
DCSOFL_IOV(since, until, output_channel, *iov_state)
 
  365             result_iov._orig_iovs = state_iovs
 
  366             dead_frac_iovs.append(result_iov)
 
  368         return dead_frac_iovs
 
  372         Make a DQ-colour decision based on `states` 
  374         states = 
set([s.Code 
for s 
in states])
 
  375         if   RED    
in states: 
return RED
 
  376         elif YELLOW 
in states: 
return YELLOW
 
  377         elif WHITE  
in states: 
return WHITE
 
  378         elif GREEN  
in states: 
return GREEN
 
  383         Merge together global states to decide a final code 
  385         If the dead fraction is unavailable, writes -1. 
  387         result_iovs = IOVSet()
 
  393             state_ranges = 
process_iovs(dead_frac_iovs, *global_variables)
 
  395         for since, until, states 
in state_ranges:
 
  398                 run_iov, dead_frac_iov = states[:2]
 
  400                 if run_iov._is_empty:
 
  406                 dead_frac_iov = states[0]
 
  408             if not dead_frac_iov._is_empty:
 
  409                 dead_fraction, thrust, n_config, n_working = dead_frac_iov[4:]
 
  411                 dead_fraction, thrust, n_config, n_working = -1., 0., -1, -1
 
  415             state = dead_fraction, thrust, n_config, n_working
 
  421             result_iovs.add(since, until, output_channel, code, *state)
 
  423         return result_iovs.solidify(DCSOFL_IOV)            
 
  426                                     local_variables, global_variables):
 
  428         Calculate the iov extents and dead fractions for one output channel 
  430         * If there are 'non-global' variables, evaluate the dead fraction, which 
  431           effectively becomes a new global variable. 
  432         * If there are no global variables, return the above as a result 
  433         * If there are global variables, merge them together. 
  436         dead_frac_iovs = IOVSet(iov_type=DCSOFL_IOV)
 
  442         if not global_variables:
 
  444             return dead_frac_iovs
 
  446         return self.
merge_globals(output_channel, dead_frac_iovs, global_variables)
 
  450         Returns a list where each element is a list of (single channel) iovs. 
  452         The `input_globals` may contain a list of iovs which has multiple  
  453         channels. This function may be over-ridden by inheriting classes to  
  454         select channels for this output channel. 
  457         for input_global 
in input_globals:
 
  458             for channel, iovs 
in sorted(input_global.by_channel.items()):
 
  459                 global_iov_sets.append(iovs)
 
  461         return global_iov_sets
 
  465         Terrible name for a method. 
  466         Calculate the iov extents and dead fractions for all output channels. 
  467         In other words, the IoVs to be written to DCSOFL for this subdetector. 
  469         result = IOVSet(iov_type=DCSOFL_IOV)
 
  472         for channel, input_iovs 
in sorted(inputs_by_output.items()):
 
  474             args = channel, input_iovs, these_globals
 
  479     def run(self, lbtime, run_iovs=None):
 
  481         Run the DCSC for this subdetector. 
  484         * Merge input variables together 
  485         * Calculate resulting IoVs to be written 
  501         local_variables  = [v.iovs 
for v 
in variables 
if not v.is_global]
 
  502         global_variables = [v.iovs 
for v 
in variables 
if v.is_global]
 
  508         if input_channel_states:
 
  513             inputs_by_output = dict((cid, []) 
for cid 
in self.mapping.
keys())
 
  523         "Empty function called at start" 
  527         An empty function which can be overloaded to do any needed  
  531 class DCSC_DefectTranslate_Subdetector(DCSC_Subdetector):
 
  533     A defect calculator for one subsystem that still works in terms of color 
  534     flags. The colors need to be translated into defects by building translators 
  535     with the color_to_defect_translator static method. 
  539         super(DCSC_DefectTranslate_Subdetector, self).
__init__()
 
  543     def run(self, lbtime, run_iovs=None):
 
  545         Run the DCSC for this subdetector. 
  548         * Merge input variables together 
  549         * Calculate resulting IoVs to be written 
  551         flag_iovs = super(DCSC_DefectTranslate_Subdetector, self).
run(lbtime, run_iovs)
 
  552         translated = 
sum((func(flag_iovs) 
for func 
in self.
translators), [])
 
  554             translated += flag_iovs
 
  559         def translator_core(flag_iovs):
 
  560             rv = [
DefectIOV(since=iov.since, until=iov.until,
 
  561                             channel=outdefect, present=
True,
 
  562                             comment=
'Bad Fraction: %.3f' % iov.deadFrac)
 
  563                   for iov 
in flag_iovs 
if iov.channel == inflag
 
  564                   and iov.Code 
in badcolors]
 
  567         return translator_core
 
  571     This calculator can be used if the calculator is only using defects 
  574     def run(self, lbtime, run_iovs):
 
  577         for variable 
in self.variables:
 
  578             result.extend(variable.iovs)