diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 6e347e850d2..ceb2184a96a 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -184,8 +184,8 @@ class BaseSelection void reset() { mFinalBitmask.reset(); - for (auto& container : mSelectionContainers) { // o2-linter: disable=const-ref-in-for-loop (object modified in loop) - container.reset(); + for (std::size_t i = 0; i < mSelectionContainers.size(); i++) { + mSelectionContainers.at(i).reset(); } if (mHasMinimalSelection) { mPassesMinimalSelections = true; @@ -386,6 +386,7 @@ class BaseSelection } LOG(info) << ""; } + LOG(info) << "Number of occupied bits: " << mNSelectionBits << " / " << sizeof(BitmaskType) * CHAR_BIT; LOG(info) << "Printing done"; } diff --git a/PWGCF/Femto/Core/closePairRejection.h b/PWGCF/Femto/Core/closePairRejection.h index c7044ceef88..c4558e5b567 100644 --- a/PWGCF/Femto/Core/closePairRejection.h +++ b/PWGCF/Femto/Core/closePairRejection.h @@ -63,6 +63,8 @@ struct ConfCpr : o2::framework::ConfigurableGroup { o2::framework::Configurable plotAverage{"plotAverage", true, "Plot average deta dphi distribution"}; o2::framework::Configurable detaMax{"detaMax", 0.01f, "Maximium deta"}; o2::framework::Configurable dphistarMax{"dphistarMax", 0.01f, "Maximum dphistar"}; + o2::framework::Configurable detaCenter{"detaCenter", 0.f, "Center of deta cut"}; + o2::framework::Configurable dphistarCenter{"dphistarCenter", 0.f, "Center of dphistar cut"}; o2::framework::Configurable kstarMin{"kstarMin", -1.f, "Minimum kstar of pair for plotting (Set to negative value to turn off the cut)"}; o2::framework::Configurable kstarMax{"kstarMax", -1.f, "Maximum kstar of pair for plotting (Set to negative value to turn off the cut)"}; o2::framework::ConfigurableAxis binningDeta{"binningDeta", {{250, -0.5, 0.5}}, "deta"}; @@ -157,6 +159,9 @@ class CloseTrackRejection LOG(fatal) << "Limits for Close Pair Rejection are invalid (0 or negative). Breaking..."; } + mDetaCenter = confCpr.detaCenter.value; + mDphistarCenter = confCpr.dphistarCenter.value; + mChargeAbsTrack1 = std::abs(chargeAbsTrack1); mChargeAbsTrack2 = std::abs(chargeAbsTrack2); @@ -280,7 +285,7 @@ class CloseTrackRejection bool isCloseAnyRadius = false; if (mCutAverage) { - isCloseAverage = std::hypot(mAverageDphistar / mDphistarMax, mDeta / mDetaMax) < 1.f; + isCloseAverage = std::hypot((mAverageDphistar - mDphistarCenter) / mDphistarMax, (mDeta - mDetaCenter) / mDetaMax) < 1.f; } if (mCutAnyRadius) { @@ -289,7 +294,7 @@ class CloseTrackRejection break; } if (mDphistarMask.at(i)) { - isCloseAnyRadius = std::hypot(mDphistar.at(i) / mDphistarMax, mDeta / mDetaMax) < 1.f; + isCloseAnyRadius = std::hypot((mDphistar.at(i) - mDphistarCenter) / mDphistarMax, (mDeta - mDetaCenter) / mDetaMax) < 1.f; } } } @@ -316,6 +321,8 @@ class CloseTrackRejection float mMagField = 0.f; float mDetaMax = 0.f; float mDphistarMax = 0.f; + float mDetaCenter = 0.f; + float mDphistarCenter = 0.f; float mAverageDphistar = 0.f; float mDeta = 0.f; diff --git a/PWGCF/Femto/Core/selectionContainer.h b/PWGCF/Femto/Core/selectionContainer.h index ca08b657d96..e9d67e6354a 100644 --- a/PWGCF/Femto/Core/selectionContainer.h +++ b/PWGCF/Femto/Core/selectionContainer.h @@ -360,6 +360,7 @@ class SelectionContainer std::ostringstream oss; std::string sectionDelimiter = ":::"; std::string valueDelimiter = "___"; + std::string noValue = "X"; oss << "SelectionName" << valueDelimiter << mSelectionName << sectionDelimiter << "LimitType" << valueDelimiter << getLimitTypeAsString() << sectionDelimiter << "MinimalCut" << valueDelimiter << (mIsMinimalCut ? "1" : "0") << sectionDelimiter @@ -368,7 +369,8 @@ class SelectionContainer << "Shift" << valueDelimiter << getShift() << sectionDelimiter << "Offset" << valueDelimiter << mOffset << sectionDelimiter << "Value" << valueDelimiter << (mSelectionFunctions.empty() ? std::to_string(mSelectionValues.at(selectionIndex)) : mSelectionFunctions.at(selectionIndex).GetExpFormula().Data()) << sectionDelimiter - << "BitPosition" << valueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? "X" : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex)); + << "BitPosition" << valueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? noValue : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex)) << sectionDelimiter + << "Comment" << valueDelimiter << (mComments.empty() ? noValue : mComments.at(selectionIndex)); return oss.str(); } diff --git a/PWGCF/Femto/Macros/cutculator.py b/PWGCF/Femto/Macros/cutculator.py index 4b522e4e220..c1c6a4bf9e3 100755 --- a/PWGCF/Femto/Macros/cutculator.py +++ b/PWGCF/Femto/Macros/cutculator.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 -#!/usr/bin/env python3 - # Copyright 2019-2025 CERN and copyright holders of ALICE O2. # See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. # All rights not expressly granted are reserved. @@ -37,50 +35,84 @@ def parse_bin_label(label): return result +def format_value_with_comment(b): + """Return Value plus optional (comment=...) suffix.""" + val = b.get("Value", "") + comment = b.get("Comment", "") + if comment and comment.upper() != "X": + return f"{val} (comment={comment})" + return val + + def ask_user_selection(group): - """Prompt user to select bin(s) for this selection group.""" + """ + Prompt user to select bin(s) for this selection group. + - If minimal selections contain exactly 1 entry → auto-select it. + - Optional selections remain user-selectable. + """ + selection_name = group[0].get("SelectionName", "unknown") + # Separate minimal and optional bins minimal_bins = [b for b in group if b.get("MinimalCut", "0") == "1" and b.get("OptionalCut", "0") == "0"] optional_bins = [b for b in group if b.get("OptionalCut", "0") == "1"] selected_bins = [] - # Minimal selection (cannot skip, 0 = loosest minimal) + # ----- Minimal selection ----- if minimal_bins: - print(f"\nSelection: {group[0].get('SelectionName', 'unknown')}") - for idx, b in enumerate(minimal_bins): - print(f" [{idx}] {b.get('Value', '')}") - while True: - sel_input = input("Enter index for minimal cut (0 = loosest minimal): ") - if sel_input.strip() == "": - sel_input = "0" - try: - sel_idx = int(sel_input) - if 0 <= sel_idx < len(minimal_bins): - selected_bins.append(minimal_bins[sel_idx]) - break - except ValueError: - pass - print("Invalid input. Please enter a valid index.") - - # Optional selection (can skip with 0) + if len(minimal_bins) == 1: + only = minimal_bins[0] + print( + f"\nSelection: {selection_name} — only one minimal option → auto-selecting: " + f"{format_value_with_comment(only)}" + ) + selected_bins.append(only) + else: + print(f"\nSelection: {selection_name}") + for idx, b in enumerate(minimal_bins): + print(f" [{idx}] {format_value_with_comment(b)}") + while True: + sel_input = input("Enter index for minimal cut (0 = loosest minimal): ") + if sel_input.strip() == "": + sel_input = "0" + try: + sel_idx = int(sel_input) + if 0 <= sel_idx < len(minimal_bins): + choice = minimal_bins[sel_idx] + selected_bins.append(choice) + print(f"Selected: {format_value_with_comment(choice)}") + break + except ValueError: + pass + print("Invalid input. Please enter a valid index.") + + # ----- Optional selection ----- if optional_bins: - print(f"Selection: {group[0].get('SelectionName', 'unknown')} (optional selection, 0 to skip)") + print(f"\nSelection: {selection_name} (optional selection, 0 to skip)") for idx, b in enumerate(optional_bins, start=1): - print(f" [{idx}] {b.get('Value', '')}") + print(f" [{idx}] {format_value_with_comment(b)}") + while True: sel_input = input("Enter indices separated by space (0 to skip): ") if not sel_input.strip() or sel_input.strip() == "0": + print("Selected: (skipped)") break + try: indices = [int(x) for x in sel_input.split()] if all(0 <= i <= len(optional_bins) for i in indices): + chosen = [] for i in indices: if i != 0: - selected_bins.append(optional_bins[i - 1]) + b = optional_bins[i - 1] + selected_bins.append(b) + chosen.append(format_value_with_comment(b)) + + print("Selected: " + ", ".join(chosen)) break except ValueError: pass + print("Invalid input. Please enter valid indices separated by space.") return selected_bins @@ -145,10 +177,22 @@ def main(rootfile_path, tdir_path="femto-producer"): continue bitmask |= 1 << int(pos) + print("\n=======================================") + print("Summary of your selections:") + print("=======================================\n") + + summary = {} + for b in selected_bins: + sel = b.get("SelectionName", "unknown") + summary.setdefault(sel, []).append(format_value_with_comment(b)) + + for sel, values in summary.items(): + print(f" {sel}: {', '.join(values)}") + print("\nFinal selected bitmask:") - print(f"Decimal: {bitmask}") - print(f"Binary: {bin(bitmask)}") - print(f"Hex: {hex(bitmask)}") + print(f" Decimal: {bitmask}") + print(f" Binary: {bin(bitmask)}") + print(f" Hex: {hex(bitmask)}") if __name__ == "__main__":