3 from DQUtils
import process_iovs
4 from DQUtils.sugar
import IOVSet, define_iov_type
5 from DQUtils.ext
import tally
8 import DCSCalculator2.config
as config
9 from .variable
import GoodIOV, DCSC_Variable_With_Mapping, DefectIOV
10 from .consts
import (WHITE, GREY, RED, YELLOW, GREEN,
11 EMPTY, OUT_OF_CONFIG, BAD, GOOD)
14 log = logging.getLogger(
"DCSCalculator2.subdetector")
15 logEnabledFor = log.isEnabledFor
19 def DCSOFL_IOV(channel, Code, deadFrac, Thrust, NConfig, NWorking):
20 "DCS calculator result iov type"
24 A defect calculator for one subdetector.
30 if not hasattr(self,
"input_to_output_map")
and hasattr(self,
"mapping"):
33 inverse = dict((value, key)
34 for key, values
in six.iteritems(self.mapping)
40 return self.__class__.__name__
44 Get a DCS_Variable by name.
46 for variable
in self.variables:
47 if variable.folder_name == name:
50 raise RuntimeError(
"Folder '%s' not found" % name)
54 Set mapping of input channels for a DCSC_Variable_With_Mapping.
55 Some input folders have different channel numbering conventions.
59 if not isinstance(variable, DCSC_Variable_With_Mapping):
60 raise RuntimeError(
"'%s' is not a DCSC_Variable_With_Mapping!")
62 variable.input_channel_map = mapping
66 Read the cool database and determine the state of the input channels
75 inputs = [v.calculate_good_iovs(lbtime, self)
for v
in self.variables]
78 if log.isEnabledFor(logging.INFO):
79 log.info(
"lbtime hash = % 09x, inputs hash = %09x",
80 hash(lbtime),
hash(tuple(self.variables)))
85 Merge input channel states across variables, taking the worst.
87 For detector configuration variables, it is assumed that if no IoV
88 exists for that channel in this variable, then it is in a good state.
101 for state, variable
in zip(states, self.variables):
102 state = state.good
if state
else None
103 if state == OUT_OF_CONFIG:
104 assert variable.is_config_variable,
"OOC without is_config_variable!"
108 elif state
is None or state < result:
109 if state
is WHITE
and variable.is_config_variable:
123 Merge multiple variables together for one input channel.
124 Each 'inputs' arg is an IOVSet that corresponds to this input channel.
133 result.add(since, until, channel, state)
135 return result.solidify(GoodIOV)
139 Join up the information which was used to make a decision across
144 info = tuple(state._orig_iov[3:]
for state
in states)
145 result.add(since, until, channel, info)
147 return result.solidify(GoodIOV)
152 Merge multiple variables together for many channels.
153 Takes a list of IOVSets, one for each DCSC_Variable.
157 info_states = IOVSet(iov_type=GoodIOV)
161 inputs = [iovs.by_channel
for iovs
in inputs]
164 all_channels =
sorted(
set(y
for x
in inputs
for y
in x.keys()))
166 tally_system_states = config.opts.tally_system_states
168 for channel
in all_channels:
171 c_inputs = [x[channel]
for x
in inputs]
176 if tally_system_states:
179 info_states.extend(info_state)
182 if tally_system_states:
184 from DQUtils.ext
import tally
186 return "/".
join(x[0]
for x
in state)
188 chans, iovs = zip(*
sorted(six.iteritems(info_states.by_channel)))
190 if states[0]._is_empty:
194 statetally =
tally(
pretty(x.good)
for x
in states[1:])
196 print(since, until, statetally)
202 Determine which input channels belong to which output channels.
203 inputs is a list of IOVSets, exactly one per channel.
212 seen_channels =
set()
214 empty_iovset_types = [iovs.empty_maker()
for iovs
in inputs]
222 if not iovs:
continue
223 input_channel = iovs[0].channel
224 if input_channel
not in mapping:
225 raise RuntimeError(
"channel not found in mapping: " +
str(input_channel))
226 seen_channels.add(input_channel)
227 output_channel = mapping[input_channel]
228 result.setdefault(output_channel, []).
append(iovs)
233 for channel, make_iovset
in zip(missing_channels, empty_iovset_types):
235 output_channel = mapping[channel]
236 result.setdefault(output_channel, []).
append(make_iovset())
245 Return a set containing the all input channel IDs for this subdetector
247 return set(v
for vals
in self.mapping.
values()
for v
in vals)
251 If it is possible to give a logical name for an input channel, return
252 it here. These numbers are used for debugging purposes.
254 By default, do nothing. Over-ridden by subdetectors
259 indices = [i
for i, x
in enumerate(states)
if x
is what]
262 return [input_chan_name(chan_indices[i])
for i
in indices]
267 Calculate the dead fraction and the resulting traffic light code.
272 n_total = len(states)
273 n_working = states.count(GOOD)
274 n_bad = states.count(BAD)
275 n_ooc = states.count(OUT_OF_CONFIG)
276 n_unfilled = states.count(EMPTY)
278 log.debug(
"%s -> %s tot:%4s working:%4s bad:%4s ooc:%4s empty:%4s",
279 since, until, n_total, n_working, n_bad, n_ooc, n_unfilled)
285 assert n_total == len(self.mapping[output_channel])
289 args = output_channel, states, EMPTY
291 log.debug(
"WARNING: the following channelids are unfilled: "
292 "%r", unfilled_chans)
296 log.debug(
"OOC ids: %s",
sorted(ooc_ids))
299 log.debug(
"BAD ids: %s",
sorted(bad_ids))
301 assert not n_total - n_working - n_bad - n_ooc - n_unfilled, (
302 "Some states unaccounted for? This is a bug.")
304 n_config = n_total - n_ooc
305 dead_fraction = 1. - n_working / n_total
308 if self.dead_fraction_caution
is not None and (
309 self.dead_fraction_caution < dead_fraction <= self.dead_fraction_bad):
312 elif dead_fraction > self.dead_fraction_bad:
317 unfilled_fraction = n_unfilled / n_total
318 threshold = (self.dead_fraction_caution
319 if self.dead_fraction_caution
is not None else
320 self.dead_fraction_bad)
321 if unfilled_fraction > threshold:
324 if n_unfilled
and config.opts.mark_unfilled_grey:
329 return code, dead_fraction, thrust, n_config, n_working
332 """Apparently not used"""
333 changes = [(a, b)
for a, b
in zip(prev_states, states)
if a != b]
334 log.debug(
"Changes at %s: %i: %s", runlb, len(changes),
tally(changes))
338 log.debug(
"Calculating dead fractions for output: %i", output_channel)
346 dead_frac_iovs = IOVSet()
353 state_iovs = states[1:]
355 states = [s.good
for s
in state_iovs]
357 if run_iov._is_empty:
361 iov_state = calc_dead_frac(since, until, output_channel,
365 result_iov =
DCSOFL_IOV(since, until, output_channel, *iov_state)
366 result_iov._orig_iovs = state_iovs
367 dead_frac_iovs.append(result_iov)
369 return dead_frac_iovs
373 Make a DQ-colour decision based on `states`
375 states =
set([s.Code
for s
in states])
376 if RED
in states:
return RED
377 elif YELLOW
in states:
return YELLOW
378 elif WHITE
in states:
return WHITE
379 elif GREEN
in states:
return GREEN
384 Merge together global states to decide a final code
386 If the dead fraction is unavailable, writes -1.
388 result_iovs = IOVSet()
394 state_ranges =
process_iovs(dead_frac_iovs, *global_variables)
396 for since, until, states
in state_ranges:
399 run_iov, dead_frac_iov = states[:2]
401 if run_iov._is_empty:
407 dead_frac_iov = states[0]
409 if not dead_frac_iov._is_empty:
410 dead_fraction, thrust, n_config, n_working = dead_frac_iov[4:]
412 dead_fraction, thrust, n_config, n_working = -1., 0., -1, -1
416 state = dead_fraction, thrust, n_config, n_working
422 result_iovs.add(since, until, output_channel, code, *state)
424 return result_iovs.solidify(DCSOFL_IOV)
427 local_variables, global_variables):
429 Calculate the iov extents and dead fractions for one output channel
431 * If there are 'non-global' variables, evaluate the dead fraction, which
432 effectively becomes a new global variable.
433 * If there are no global variables, return the above as a result
434 * If there are global variables, merge them together.
437 dead_frac_iovs = IOVSet(iov_type=DCSOFL_IOV)
443 if not global_variables:
445 return dead_frac_iovs
447 return self.
merge_globals(output_channel, dead_frac_iovs, global_variables)
451 Returns a list where each element is a list of (single channel) iovs.
453 The `input_globals` may contain a list of iovs which has multiple
454 channels. This function may be over-ridden by inheriting classes to
455 select channels for this output channel.
458 for input_global
in input_globals:
459 for channel, iovs
in sorted(six.iteritems(input_global.by_channel)):
460 global_iov_sets.append(iovs)
462 return global_iov_sets
466 Terrible name for a method.
467 Calculate the iov extents and dead fractions for all output channels.
468 In other words, the IoVs to be written to DCSOFL for this subdetector.
470 result = IOVSet(iov_type=DCSOFL_IOV)
473 for channel, input_iovs
in sorted(six.iteritems(inputs_by_output)):
475 args = channel, input_iovs, these_globals
480 def run(self, lbtime, run_iovs=None):
482 Run the DCSC for this subdetector.
485 * Merge input variables together
486 * Calculate resulting IoVs to be written
502 local_variables = [v.iovs
for v
in variables
if not v.is_global]
503 global_variables = [v.iovs
for v
in variables
if v.is_global]
509 if input_channel_states:
514 inputs_by_output = dict((cid, [])
for cid
in self.mapping.
keys())
524 "Empty function called at start"
528 An empty function which can be overloaded to do any needed
532 class DCSC_DefectTranslate_Subdetector(DCSC_Subdetector):
534 A defect calculator for one subsystem that still works in terms of color
535 flags. The colors need to be translated into defects by building translators
536 with the color_to_defect_translator static method.
540 super(DCSC_DefectTranslate_Subdetector, self).
__init__()
544 def run(self, lbtime, run_iovs=None):
546 Run the DCSC for this subdetector.
549 * Merge input variables together
550 * Calculate resulting IoVs to be written
552 flag_iovs = super(DCSC_DefectTranslate_Subdetector, self).
run(lbtime, run_iovs)
553 translated =
sum((func(flag_iovs)
for func
in self.
translators), [])
555 translated += flag_iovs
560 def translator_core(flag_iovs):
561 rv = [
DefectIOV(since=iov.since, until=iov.until,
562 channel=outdefect, present=
True,
563 comment=
'Bad Fraction: %.3f' % iov.deadFrac)
564 for iov
in flag_iovs
if iov.channel == inflag
565 and iov.Code
in badcolors]
568 return translator_core
572 This calculator can be used if the calculator is only using defects
575 def run(self, lbtime, run_iovs):
578 for variable
in self.variables:
579 result.extend(variable.iovs)