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)